5134 lines
		
	
	
		
			160 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			5134 lines
		
	
	
		
			160 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // This is an independent project of an individual developer. Dear PVS-Studio, please check it.
 | |
| // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
 | |
| 
 | |
| /*
 | |
| MIT License
 | |
| 
 | |
| Copyright (c) 2019-2020 Stephane Cuillerdier (aka aiekick)
 | |
| 
 | |
| Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| of this software and associated documentation files (the "Software"), to deal
 | |
| in the Software without restriction, including without limitation the rights
 | |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| copies of the Software, and to permit persons to whom the Software is
 | |
| furnished to do so, subject to the following conditions:
 | |
| 
 | |
| The above copyright notice and this permission notice shall be included in all
 | |
| copies or substantial portions of the Software.
 | |
| 
 | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
| SOFTWARE.
 | |
| */
 | |
| 
 | |
| #ifndef IMGUI_DEFINE_MATH_OPERATORS
 | |
|   #define IMGUI_DEFINE_MATH_OPERATORS
 | |
| #endif // IMGUI_DEFINE_MATH_OPERATORS
 | |
| 
 | |
| #include "ImGuiFileDialog.h"
 | |
| #include "../../src/ta-log.h"
 | |
| 
 | |
| #ifdef __cplusplus
 | |
| 
 | |
| #include <cfloat>
 | |
| #include <cstring> // stricmp / strcasecmp
 | |
| #include <cstdarg> // variadic
 | |
| #include <sstream>
 | |
| #include <iomanip>
 | |
| #include <ctime>
 | |
| #include <sys/stat.h>
 | |
| #include <cstdio>
 | |
| #if defined (__EMSCRIPTEN__) // EMSCRIPTEN
 | |
|   #include <emscripten.h>
 | |
| #endif // EMSCRIPTEN
 | |
| #ifdef WIN32
 | |
|   #define stricmp _stricmp
 | |
|   #include <cctype>
 | |
|   #include "dirent/dirent.h" // directly open the dirent file attached to this lib
 | |
|   #define PATH_SEP '\\'
 | |
|   #define PATH_SEP_STR "\\"
 | |
|   #ifndef PATH_MAX
 | |
|     #define PATH_MAX 260
 | |
|   #endif // PATH_MAX
 | |
| #elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined (__EMSCRIPTEN__) || defined(__HAIKU__)
 | |
|   #define UNIX
 | |
|   #define stricmp strcasecmp
 | |
|   #include <sys/types.h>
 | |
|   #include <dirent.h>
 | |
|   #define PATH_SEP '/'
 | |
|   #define PATH_SEP_STR "/"
 | |
| #endif // defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
 | |
| 
 | |
| #include "imgui.h"
 | |
| #include "imgui_internal.h"
 | |
| #include <IconsFontAwesome4.h>
 | |
| 
 | |
| #include <cstdlib>
 | |
| #include <algorithm>
 | |
| #include <iostream>
 | |
| 
 | |
| #define DOUBLE_CLICKED ((singleClickSel && ImGui::IsMouseReleased(0)) || (!singleClickSel && ImGui::IsMouseDoubleClicked(0)))
 | |
| 
 | |
| #ifdef USE_THUMBNAILS
 | |
| #ifndef DONT_DEFINE_AGAIN__STB_IMAGE_IMPLEMENTATION
 | |
| #ifndef STB_IMAGE_IMPLEMENTATION
 | |
| #define STB_IMAGE_IMPLEMENTATION
 | |
| #endif // STB_IMAGE_IMPLEMENTATION
 | |
| #endif // DONT_DEFINE_AGAIN__STB_IMAGE_IMPLEMENTATION
 | |
| #include "stb/stb_image.h"
 | |
| #ifndef DONT_DEFINE_AGAIN__STB_IMAGE_RESIZE_IMPLEMENTATION
 | |
| #ifndef STB_IMAGE_RESIZE_IMPLEMENTATION
 | |
| #define STB_IMAGE_RESIZE_IMPLEMENTATION
 | |
| #endif // STB_IMAGE_RESIZE_IMPLEMENTATION
 | |
| #endif // DONT_DEFINE_AGAIN__STB_IMAGE_RESIZE_IMPLEMENTATION
 | |
| #include "stb/stb_image_resize.h"
 | |
| #endif // USE_THUMBNAILS
 | |
| 
 | |
| namespace IGFD
 | |
| {
 | |
| // float comparisons
 | |
| #ifndef IS_FLOAT_DIFFERENT
 | |
| #define IS_FLOAT_DIFFERENT(a,b) (fabs((a) - (b)) > FLT_EPSILON)
 | |
| #endif // IS_FLOAT_DIFFERENT
 | |
| #ifndef IS_FLOAT_EQUAL
 | |
| #define IS_FLOAT_EQUAL(a,b) (fabs((a) - (b)) < FLT_EPSILON)
 | |
| #endif // IS_FLOAT_EQUAL
 | |
| // width of filter combobox
 | |
| #ifndef FILTER_COMBO_WIDTH
 | |
| #define FILTER_COMBO_WIDTH 150.0f
 | |
| #endif // FILTER_COMBO_WIDTH
 | |
| #ifndef IMGUI_BUTTON
 | |
| #define IMGUI_BUTTON ImGui::Button
 | |
| #endif // IMGUI_BUTTON
 | |
| // locales
 | |
| #ifndef createDirButtonString
 | |
| #define createDirButtonString ICON_FA_PLUS
 | |
| #endif // createDirButtonString
 | |
| #ifndef resetButtonString
 | |
| #define resetButtonString ICON_FA_REPEAT
 | |
| #endif // resetButtonString
 | |
| #ifndef homeButtonString
 | |
| #define homeButtonString ICON_FA_HOME
 | |
| #endif // bomeButtonString
 | |
| #ifndef drivesButtonString
 | |
| #define drivesButtonString ICON_FA_HDD_O
 | |
| #endif // drivesButtonString
 | |
| #ifndef parentDirString
 | |
| #define parentDirString ICON_FA_CHEVRON_UP
 | |
| #endif // parentDirString
 | |
| #ifndef editPathButtonString
 | |
| #define editPathButtonString ICON_FA_PENCIL
 | |
| #endif // editPathButtonString
 | |
| #ifdef USE_THUMBNAILS
 | |
| #ifndef tableHeaderFileThumbnailsString
 | |
| #define tableHeaderFileThumbnailsString "Thumbnails"
 | |
| #endif // tableHeaderFileThumbnailsString
 | |
| #ifndef DisplayMode_FilesList_ButtonString
 | |
| #define DisplayMode_FilesList_ButtonString "FL"
 | |
| #endif // DisplayMode_FilesList_ButtonString
 | |
| #ifndef DisplayMode_FilesList_ButtonHelp
 | |
| #define DisplayMode_FilesList_ButtonHelp "File List"
 | |
| #endif // DisplayMode_FilesList_ButtonHelp
 | |
| #ifndef DisplayMode_ThumbailsList_ButtonString
 | |
| #define DisplayMode_ThumbailsList_ButtonString "TL"
 | |
| #endif // DisplayMode_ThumbailsList_ButtonString
 | |
| #ifndef DisplayMode_ThumbailsList_ButtonHelp
 | |
| #define DisplayMode_ThumbailsList_ButtonHelp "Thumbnails List"
 | |
| #endif // DisplayMode_ThumbailsList_ButtonHelp
 | |
| #ifndef DisplayMode_ThumbailsGrid_ButtonString
 | |
| #define DisplayMode_ThumbailsGrid_ButtonString "TG"
 | |
| #endif // DisplayMode_ThumbailsGrid_ButtonString
 | |
| #ifndef DisplayMode_ThumbailsGrid_ButtonHelp
 | |
| #define DisplayMode_ThumbailsGrid_ButtonHelp "Thumbnails Grid"
 | |
| #endif // DisplayMode_ThumbailsGrid_ButtonHelp
 | |
| #ifndef DisplayMode_ThumbailsList_ImageHeight 
 | |
| #define DisplayMode_ThumbailsList_ImageHeight 32.0f
 | |
| #endif // DisplayMode_ThumbailsList_ImageHeight
 | |
| #ifndef IMGUI_RADIO_BUTTON
 | |
|   inline bool inRadioButton(const char* vLabel, bool vToggled)
 | |
|   {
 | |
|     bool pressed = false;
 | |
| 
 | |
|     if (vToggled)
 | |
|     {
 | |
|       ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
 | |
|       ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text);
 | |
|       ImGui::PushStyleColor(ImGuiCol_Button, te);
 | |
|       ImGui::PushStyleColor(ImGuiCol_ButtonActive, te);
 | |
|       ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te);
 | |
|       ImGui::PushStyleColor(ImGuiCol_Text, bua);
 | |
|     }
 | |
| 
 | |
|     pressed = IMGUI_BUTTON(vLabel);
 | |
| 
 | |
|     if (vToggled)
 | |
|     {
 | |
|       ImGui::PopStyleColor(4); //-V112
 | |
|     }
 | |
| 
 | |
|     return pressed;
 | |
|   }
 | |
| #define IMGUI_RADIO_BUTTON inRadioButton
 | |
| #endif // IMGUI_RADIO_BUTTON
 | |
| #endif  // USE_THUMBNAILS
 | |
| #ifdef USE_BOOKMARK
 | |
| #ifndef defaultBookmarkPaneWith
 | |
| #define defaultBookmarkPaneWith 150.0f
 | |
| #endif // defaultBookmarkPaneWith
 | |
| #ifndef bookmarksButtonString
 | |
| #define bookmarksButtonString "Bookmark"
 | |
| #endif // bookmarksButtonString
 | |
| #ifndef bookmarksButtonHelpString
 | |
| #define bookmarksButtonHelpString "Bookmark"
 | |
| #endif // bookmarksButtonHelpString
 | |
| #ifndef addBookmarkButtonString
 | |
| #define addBookmarkButtonString "+"
 | |
| #endif // addBookmarkButtonString
 | |
| #ifndef removeBookmarkButtonString
 | |
| #define removeBookmarkButtonString "-"
 | |
| #endif // removeBookmarkButtonString
 | |
| #ifndef IMGUI_TOGGLE_BUTTON
 | |
|   inline bool inToggleButton(const char* vLabel, bool* vToggled)
 | |
|   {
 | |
|     bool pressed = false;
 | |
| 
 | |
|     if (vToggled && *vToggled)
 | |
|     {
 | |
|       ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
 | |
|       //ImVec4 buh = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);
 | |
|       //ImVec4 bu = ImGui::GetStyleColorVec4(ImGuiCol_Button);
 | |
|       ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text);
 | |
|       ImGui::PushStyleColor(ImGuiCol_Button, te);
 | |
|       ImGui::PushStyleColor(ImGuiCol_ButtonActive, te);
 | |
|       ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te);
 | |
|       ImGui::PushStyleColor(ImGuiCol_Text, bua);
 | |
|     }
 | |
| 
 | |
|     pressed = IMGUI_BUTTON(vLabel);
 | |
| 
 | |
|     if (vToggled && *vToggled)
 | |
|     {
 | |
|       ImGui::PopStyleColor(4); //-V112
 | |
|     }
 | |
| 
 | |
|     if (vToggled && pressed)
 | |
|       *vToggled = !*vToggled;
 | |
| 
 | |
|     return pressed;
 | |
|   }
 | |
| #define IMGUI_TOGGLE_BUTTON inToggleButton
 | |
| #endif // IMGUI_TOGGLE_BUTTON
 | |
| #endif // USE_BOOKMARK
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// INLINE FUNCTIONS ///////////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   inline int inAlphaSort(const struct dirent** a, const struct dirent** b)
 | |
|   {
 | |
|     return strcoll((*a)->d_name, (*b)->d_name);
 | |
|   }
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// FILE EXTENTIONS INFOS //////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   IGFD::FileStyle::FileStyle() 
 | |
|     : color(0, 0, 0, 0)
 | |
|   { 
 | |
| 
 | |
|   }
 | |
|   
 | |
|   IGFD::FileStyle::FileStyle(const FileStyle& vStyle)
 | |
|   {
 | |
|     color = vStyle.color;
 | |
|     icon = vStyle.icon;
 | |
|     font = vStyle.font;
 | |
|     flags = vStyle.flags;
 | |
|   }
 | |
| 
 | |
|   IGFD::FileStyle::FileStyle(const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) 
 | |
|     : color(vColor), icon(vIcon), font(vFont)
 | |
|   { 
 | |
| 
 | |
|   }
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// FILE INFOS /////////////////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   // https://github.com/ocornut/imgui/issues/1720
 | |
|   bool IGFD::Utils::Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size)
 | |
|   {
 | |
|     using namespace ImGui;
 | |
|     ImGuiContext& g = *GImGui;
 | |
|     ImGuiWindow* window = g.CurrentWindow;
 | |
|     ImGuiID id = window->GetID("##Splitter");
 | |
|     ImRect bb;
 | |
|     bb.Min = window->DC.CursorPos + (split_vertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1));
 | |
|     bb.Max = bb.Min + CalcItemSize(split_vertically ? ImVec2(thickness, splitter_long_axis_size) : ImVec2(splitter_long_axis_size, thickness), 0.0f, 0.0f);
 | |
|     return SplitterBehavior(bb, id, split_vertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, min_size1, min_size2, 1.0f);
 | |
|   }
 | |
| 
 | |
| #ifdef WIN32
 | |
|   bool IGFD::Utils::WReplaceString(std::wstring& str, const std::wstring& oldStr, const std::wstring& newStr)
 | |
|   {
 | |
|     bool found = false;
 | |
|     size_t pos = 0;
 | |
|     while ((pos = str.find(oldStr, pos)) != std::wstring::npos)
 | |
|     {
 | |
|       found = true;
 | |
|       str.replace(pos, oldStr.length(), newStr);
 | |
|       pos += newStr.length();
 | |
|     }
 | |
|     return found;
 | |
|   }
 | |
| 
 | |
|   std::vector<std::wstring> IGFD::Utils::WSplitStringToVector(const std::wstring& text, char delimiter, bool pushEmpty)
 | |
|   {
 | |
|     std::vector<std::wstring> arr;
 | |
|     if (!text.empty())
 | |
|     {
 | |
|       std::wstring::size_type start = 0;
 | |
|       std::wstring::size_type end = text.find(delimiter, start);
 | |
|       while (end != std::wstring::npos)
 | |
|       {
 | |
|         std::wstring token = text.substr(start, end - start);
 | |
|         if (!token.empty() || (token.empty() && pushEmpty)) //-V728
 | |
|           arr.push_back(token);
 | |
|         start = end + 1;
 | |
|         end = text.find(delimiter, start);
 | |
|       }
 | |
|       std::wstring token = text.substr(start);
 | |
|       if (!token.empty() || (token.empty() && pushEmpty)) //-V728
 | |
|         arr.push_back(token);
 | |
|     }
 | |
|     return arr;
 | |
|   }
 | |
| 
 | |
|   std::wstring IGFD::Utils::string_to_wstring(const std::string& str)
 | |
|   {
 | |
|     std::wstring ret;
 | |
|     if (!str.empty())
 | |
|     {
 | |
|       size_t sz = std::mbstowcs(nullptr, str.c_str(), str.size());
 | |
|       if (sz)
 | |
|       {
 | |
|         ret.resize(sz);
 | |
|         std::mbstowcs((wchar_t*)ret.data(), str.c_str(), sz);
 | |
|       }
 | |
|     }
 | |
|     return ret;
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::Utils::wstring_to_string(const std::wstring& str)
 | |
|   {
 | |
|     std::string ret;
 | |
|     if (!str.empty())
 | |
|     {
 | |
|       size_t sz = std::wcstombs(nullptr, str.c_str(), str.size());
 | |
|       if (sz)
 | |
|       {
 | |
|         ret.resize(sz);
 | |
|         std::wcstombs((char*)ret.data(), str.c_str(), sz);
 | |
|       }
 | |
|     }
 | |
|     return ret;
 | |
|   }
 | |
| #endif // WIN32
 | |
| 
 | |
|   bool IGFD::Utils::ReplaceString(std::string& str, const std::string& oldStr, const std::string& newStr)
 | |
|   {
 | |
|     bool found = false;
 | |
|     size_t pos = 0;
 | |
|     while ((pos = str.find(oldStr, pos)) != std::string::npos)
 | |
|     {
 | |
|       found = true;
 | |
|       str.replace(pos, oldStr.length(), newStr);
 | |
|       pos += newStr.length();
 | |
|     }
 | |
|     return found;
 | |
|   }
 | |
| 
 | |
|   std::vector<std::string> IGFD::Utils::SplitStringToVector(const std::string& text, char delimiter, bool pushEmpty)
 | |
|   {
 | |
|     std::vector<std::string> arr;
 | |
|     if (!text.empty())
 | |
|     {
 | |
|       size_t start = 0;
 | |
|       size_t end = text.find(delimiter, start);
 | |
|       while (end != std::string::npos)
 | |
|       {
 | |
|         auto token = text.substr(start, end - start);
 | |
|         if (!token.empty() || (token.empty() && pushEmpty)) //-V728
 | |
|           arr.push_back(token);
 | |
|         start = end + 1;
 | |
|         end = text.find(delimiter, start);
 | |
|       }
 | |
|       auto token = text.substr(start);
 | |
|       if (!token.empty() || (token.empty() && pushEmpty)) //-V728
 | |
|         arr.push_back(token);
 | |
|     }
 | |
|     return arr;
 | |
|   }
 | |
| 
 | |
|   std::vector<std::string> IGFD::Utils::GetDrivesList()
 | |
|   {
 | |
|     std::vector<std::string> res;
 | |
| 
 | |
| #ifdef WIN32
 | |
|     const DWORD mydrives = 2048;
 | |
|     char lpBuffer[2048];
 | |
| #define mini(a,b) (((a) < (b)) ? (a) : (b))
 | |
|     const DWORD countChars = mini(GetLogicalDriveStringsA(mydrives, lpBuffer), 2047);
 | |
| #undef mini
 | |
|     if (countChars > 0)
 | |
|     {
 | |
|       std::string var = std::string(lpBuffer, (size_t)countChars);
 | |
|       IGFD::Utils::ReplaceString(var, "\\", "");
 | |
|       res = IGFD::Utils::SplitStringToVector(var, '\0', false);
 | |
|     }
 | |
| #endif // WIN32
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::Utils::IsDirectoryExist(const std::string& name)
 | |
|   {
 | |
|     bool bExists = false;
 | |
| 
 | |
|     if (!name.empty())
 | |
|     {
 | |
|       DIR* pDir = nullptr;
 | |
|       pDir = opendir(name.c_str());
 | |
|       if (pDir != nullptr)
 | |
|       {
 | |
|         bExists = true;
 | |
|         (void)closedir(pDir);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return bExists;    // this is not a directory!
 | |
|   }
 | |
| 
 | |
|   bool IGFD::Utils::CreateDirectoryIfNotExist(const std::string& name)
 | |
|   {
 | |
|     bool res = false;
 | |
| 
 | |
|     if (!name.empty())
 | |
|     {
 | |
|       if (!IsDirectoryExist(name))
 | |
|       {
 | |
| #ifdef WIN32
 | |
|         std::wstring wname = IGFD::Utils::string_to_wstring(name);
 | |
|         if (CreateDirectoryW(wname.c_str(), nullptr))
 | |
|         {
 | |
|           res = true;
 | |
|         }
 | |
| #elif defined(__EMSCRIPTEN__)
 | |
|         std::string str = std::string("FS.mkdir('") + name + "');";
 | |
|         emscripten_run_script(str.c_str());
 | |
|         res = true;
 | |
| #elif defined(UNIX)
 | |
|         char buffer[PATH_MAX] = {};
 | |
|         snprintf(buffer, PATH_MAX, "mkdir -p %s", name.c_str());
 | |
|         const int dir_err = std::system(buffer);
 | |
|         if (dir_err != -1)
 | |
|         {
 | |
|           res = true;
 | |
|         }
 | |
| #endif // WIN32
 | |
|         if (!res) {
 | |
|           std::cout << "Error creating directory " << name << std::endl;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   IGFD::Utils::PathStruct IGFD::Utils::ParsePathFileName(const std::string& vPathFileName)
 | |
|   {
 | |
|     PathStruct res;
 | |
| 
 | |
|     if (!vPathFileName.empty())
 | |
|     {
 | |
|       std::string pfn = vPathFileName;
 | |
|       std::string separator(1u, PATH_SEP);
 | |
|       IGFD::Utils::ReplaceString(pfn, "\\", separator);
 | |
|       IGFD::Utils::ReplaceString(pfn, "/", separator);
 | |
| 
 | |
|       size_t lastSlash = pfn.find_last_of(separator);
 | |
|       if (lastSlash != std::string::npos)
 | |
|       {
 | |
|         res.name = pfn.substr(lastSlash + 1);
 | |
|         res.path = pfn.substr(0, lastSlash);
 | |
|         res.isOk = true;
 | |
|       }
 | |
| 
 | |
|       size_t lastPoint = pfn.find_last_of('.');
 | |
|       if (lastPoint != std::string::npos)
 | |
|       {
 | |
|         if (!res.isOk)
 | |
|         {
 | |
|           res.name = pfn;
 | |
|           res.isOk = true;
 | |
|         }
 | |
|         res.ext = pfn.substr(lastPoint + 1);
 | |
|         IGFD::Utils::ReplaceString(res.name, "." + res.ext, "");
 | |
|       }
 | |
|       
 | |
|       if (res.path.empty()) {
 | |
|         res.path=separator;
 | |
|       }
 | |
| 
 | |
|       if (!res.isOk)
 | |
|       {
 | |
|         res.name = std::move(pfn);
 | |
|         res.isOk = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
|   void IGFD::Utils::AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr)
 | |
|   {
 | |
|     std::string st = vStr;
 | |
|     size_t len = vBufferLen - 1u;
 | |
|     size_t slen = strlen(vBuffer);
 | |
| 
 | |
|     if (!st.empty() && st != "\n")
 | |
|     {
 | |
|       IGFD::Utils::ReplaceString(st, "\n", "");
 | |
|       IGFD::Utils::ReplaceString(st, "\r", "");
 | |
|     }
 | |
|     vBuffer[slen] = '\0';
 | |
|     std::string str = std::string(vBuffer);
 | |
|     //if (!str.empty()) str += "\n";
 | |
|     str += vStr;
 | |
|     if (len > str.size()) len = str.size();
 | |
| #ifdef MSVC
 | |
|     strncpy_s(vBuffer, vBufferLen, str.c_str(), len);
 | |
| #else // MSVC
 | |
|     strncpy(vBuffer, str.c_str(), len);
 | |
| #endif // MSVC
 | |
|     vBuffer[len] = '\0';
 | |
|   }
 | |
| 
 | |
|   void IGFD::Utils::ResetBuffer(char* vBuffer)
 | |
|   {
 | |
|     vBuffer[0] = '\0';
 | |
|   }
 | |
| 
 | |
|   void IGFD::Utils::SetBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr)
 | |
|   {
 | |
|     ResetBuffer(vBuffer);
 | |
|     AppendToBuffer(vBuffer, vBufferLen, vStr);
 | |
|   }
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// FILE INFOS /////////////////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   bool IGFD::FileInfos::IsTagFound(const std::string& vTag) const
 | |
|   {
 | |
|     if (!vTag.empty())
 | |
|     {
 | |
|       if (fileNameExt_optimized == "..") return true;
 | |
| 
 | |
|       return
 | |
|         fileNameExt_optimized.find(vTag) != std::string::npos ||  // first try wihtout case and accents
 | |
|         fileNameExt.find(vTag) != std::string::npos;        // second if searched with case and accents
 | |
|     }
 | |
| 
 | |
|     // if tag is empty => its a special case but all is found
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// SEARCH MANAGER /////////////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   void IGFD::SearchManager::Clear()
 | |
|   {
 | |
|     puSearchTag.clear();
 | |
|     IGFD::Utils::ResetBuffer(puSearchBuffer);
 | |
|   }
 | |
| 
 | |
|   void IGFD::SearchManager::DrawSearchBar(FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     // search field
 | |
|     if (IMGUI_BUTTON(resetButtonString "##BtnImGuiFileDialogSearchField"))
 | |
|     {
 | |
|       Clear();
 | |
|       vFileDialogInternal.puFileManager.ApplyFilteringOnFileList(vFileDialogInternal);
 | |
|     }
 | |
|     if (ImGui::IsItemHovered())
 | |
|       ImGui::SetTooltip("%s",FileDialog::Instance()->buttonResetSearchString);
 | |
|     ImGui::SameLine();
 | |
|     ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
 | |
|     bool edited = ImGui::InputTextWithHint("##InputImGuiFileDialogSearchField", FileDialog::Instance()->searchString, puSearchBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
 | |
|     if (ImGui::GetItemID() == ImGui::GetActiveID())
 | |
|       puSearchInputIsActive = true;
 | |
|     ImGui::PopItemWidth();
 | |
|     if (edited)
 | |
|     {
 | |
|       puSearchTag = puSearchBuffer;
 | |
|       vFileDialogInternal.puFileManager.ApplyFilteringOnFileList(vFileDialogInternal);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// FILTER INFOS ///////////////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   void IGFD::FilterManager::FilterInfos::clear()
 | |
|   {
 | |
|     filter.clear(); 
 | |
|     collectionfilters.clear();
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FilterManager::FilterInfos::empty() const
 | |
|   {
 | |
|     return filter.empty() && collectionfilters.empty();
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FilterManager::FilterInfos::exist(const std::string& vFilter) const
 | |
|   {
 | |
|     return filter == vFilter || (collectionfilters.find(vFilter) != collectionfilters.end());
 | |
|   }
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// FILTER MANAGER /////////////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   void IGFD::FilterManager::ParseFilters(const char* vFilters)
 | |
|   {
 | |
|     prParsedFilters.clear();
 | |
| 
 | |
|     if (vFilters)
 | |
|       puDLGFilters = vFilters;      // file mode
 | |
|     else
 | |
|       puDLGFilters.clear();        // directory mode
 | |
| 
 | |
|     if (!puDLGFilters.empty())
 | |
|     {
 | |
|       // ".*,.cpp,.h,.hpp"
 | |
|       // "Source files{.cpp,.h,.hpp},Image files{.png,.gif,.jpg,.jpeg},.md"
 | |
| 
 | |
|       bool currentFilterFound = false;
 | |
| 
 | |
|       size_t nan = std::string::npos;
 | |
|       size_t p = 0, lp = 0;
 | |
|       while ((p = puDLGFilters.find_first_of("{,", p)) != nan)
 | |
|       {
 | |
|         FilterInfos infos;
 | |
| 
 | |
|         if (puDLGFilters[p] == '{') // {
 | |
|         {
 | |
|           infos.filter = puDLGFilters.substr(lp, p - lp);
 | |
|           p++;
 | |
|           lp = puDLGFilters.find('}', p);
 | |
|           if (lp != nan)
 | |
|           {
 | |
|             std::string fs = puDLGFilters.substr(p, lp - p);
 | |
|             auto arr = IGFD::Utils::SplitStringToVector(fs, ',', false);
 | |
|             for (auto a : arr)
 | |
|             {
 | |
|               infos.firstFilter=a;
 | |
|               infos.collectionfilters.emplace(a);
 | |
|             }
 | |
|           }
 | |
|           p = lp + 1;
 | |
|         }
 | |
|         else // ,
 | |
|         {
 | |
|           infos.filter = puDLGFilters.substr(lp, p - lp);
 | |
|           p++;
 | |
|         }
 | |
| 
 | |
|         if (!currentFilterFound && prSelectedFilter.filter == infos.filter)
 | |
|         {
 | |
|           currentFilterFound = true;
 | |
|           prSelectedFilter = infos;
 | |
|         }
 | |
| 
 | |
|         lp = p;
 | |
|         if (!infos.empty())
 | |
|           prParsedFilters.emplace_back(infos);
 | |
|       }
 | |
| 
 | |
|       std::string token = puDLGFilters.substr(lp);
 | |
|       if (!token.empty())
 | |
|       {
 | |
|         FilterInfos infos;
 | |
|         infos.filter = std::move(token);
 | |
|         prParsedFilters.emplace_back(infos);
 | |
|       }
 | |
| 
 | |
|       if (!currentFilterFound)
 | |
|         if (!prParsedFilters.empty())
 | |
|           prSelectedFilter = *prParsedFilters.begin();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::FilterManager::SetSelectedFilterWithExt(const std::string& vFilter)
 | |
|   {
 | |
|     if (!prParsedFilters.empty())
 | |
|     {
 | |
|       if (!vFilter.empty())
 | |
|       {
 | |
|         // std::map<std::string, FilterInfos>
 | |
|         for (const auto& infos : prParsedFilters)
 | |
|         {
 | |
|           if (vFilter == infos.filter)
 | |
|           {
 | |
|             prSelectedFilter = infos;
 | |
|           }
 | |
|           else
 | |
|           {
 | |
|             // maybe this ext is in an extention so we will 
 | |
|             // explore the collections is they are existing
 | |
|             for (const auto& filter : infos.collectionfilters)
 | |
|             {
 | |
|               if (vFilter == filter)
 | |
|               {
 | |
|                 prSelectedFilter = infos;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (prSelectedFilter.empty())
 | |
|         prSelectedFilter = *prParsedFilters.begin();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos)
 | |
|   {
 | |
|     std::string _criteria;
 | |
|     if (vCriteria)
 | |
|       _criteria = std::string(vCriteria);
 | |
|     prFilesStyle[vFlags][_criteria] = std::make_shared<FileStyle>(vInfos);
 | |
|     prFilesStyle[vFlags][_criteria]->flags = vFlags;
 | |
|   }
 | |
| 
 | |
|   // will be called internally 
 | |
|   // will not been exposed to IGFD API
 | |
|   bool IGFD::FilterManager::prFillFileStyle(FileInfos& vFileInfos) const
 | |
|   {
 | |
|     if (!prFilesStyle.empty())
 | |
|     {
 | |
|       for (const auto& _flag : prFilesStyle)
 | |
|       {
 | |
|         for (const auto& _file : _flag.second)
 | |
|         {
 | |
|           if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
 | |
|           {
 | |
|             if (_file.first.empty()) // for all dirs
 | |
|             {
 | |
|               vFileInfos.fileStyle = _file.second;
 | |
|             }
 | |
|             else if (_file.first == vFileInfos.fileNameExt) // for dirs who are equal to style criteria
 | |
|             {
 | |
|               vFileInfos.fileStyle = _file.second;
 | |
|             }
 | |
|           }
 | |
|           else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
 | |
|           {
 | |
|             if (_file.first.empty()) // for all files
 | |
|             {
 | |
|               vFileInfos.fileStyle = _file.second;
 | |
|             }
 | |
|             else if (_file.first == vFileInfos.fileNameExt) // for files who are equal to style criteria
 | |
|             {
 | |
|               vFileInfos.fileStyle = _file.second;
 | |
|             }
 | |
|           }
 | |
|           else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
 | |
|           {
 | |
|             if (_file.first.empty()) // for all links
 | |
|             {
 | |
|               vFileInfos.fileStyle = _file.second;
 | |
|             }
 | |
|             else if (_file.first == vFileInfos.fileNameExt) // for links who are equal to style criteria
 | |
|             {
 | |
|               vFileInfos.fileStyle = _file.second;
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           if (_flag.first & IGFD_FileStyleByExtention)
 | |
|           {
 | |
|             if (_file.first == vFileInfos.fileExt)
 | |
|             {
 | |
|               vFileInfos.fileStyle = _file.second;
 | |
|             }
 | |
| 
 | |
|             // can make sense for some dirs like the hidden by ex ".git"
 | |
|             if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
 | |
|             {
 | |
|               if (_file.first == vFileInfos.fileExt)
 | |
|               {
 | |
|                 vFileInfos.fileStyle = _file.second;
 | |
|               }
 | |
|             }
 | |
|             else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
 | |
|             {
 | |
|               if (_file.first == vFileInfos.fileExt)
 | |
|               {
 | |
|                 vFileInfos.fileStyle = _file.second;
 | |
|               }
 | |
|             }
 | |
|             else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
 | |
|             {
 | |
|               if (_file.first == vFileInfos.fileExt)
 | |
|               {
 | |
|                 vFileInfos.fileStyle = _file.second;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|           if (_flag.first & IGFD_FileStyleByFullName)
 | |
|           {
 | |
|             if (_file.first == vFileInfos.fileNameExt)
 | |
|             {
 | |
|               vFileInfos.fileStyle = _file.second;
 | |
|             }
 | |
| 
 | |
|             if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
 | |
|             {
 | |
|               if (_file.first == vFileInfos.fileNameExt)
 | |
|               {
 | |
|                 vFileInfos.fileStyle = _file.second;
 | |
|               }
 | |
|             }
 | |
|             else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
 | |
|             {
 | |
|               if (_file.first == vFileInfos.fileNameExt)
 | |
|               {
 | |
|                 vFileInfos.fileStyle = _file.second;
 | |
|               }
 | |
|             }
 | |
|             else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
 | |
|             {
 | |
|               if (_file.first == vFileInfos.fileNameExt)
 | |
|               {
 | |
|                 vFileInfos.fileStyle = _file.second;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|           if (_flag.first & IGFD_FileStyleByContainedInFullName)
 | |
|           {
 | |
|             if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
 | |
|             {
 | |
|               vFileInfos.fileStyle = _file.second;
 | |
|             }
 | |
| 
 | |
|             if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
 | |
|             {
 | |
|               if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
 | |
|               {
 | |
|                 vFileInfos.fileStyle = _file.second;
 | |
|               }
 | |
|             }
 | |
|             else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
 | |
|             {
 | |
|               if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
 | |
|               {
 | |
|                 vFileInfos.fileStyle = _file.second;
 | |
|               }
 | |
|             }
 | |
|             else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
 | |
|             {
 | |
|               if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
 | |
|               {
 | |
|                 vFileInfos.fileStyle = _file.second;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           if (vFileInfos.fileStyle.use_count())
 | |
|             return true;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont)
 | |
|   {
 | |
|     std::string _criteria;
 | |
|     if (vCriteria)
 | |
|       _criteria = std::string(vCriteria);
 | |
|     prFilesStyle[vFlags][_criteria] = std::make_shared<FileStyle>(vColor, vIcon, vFont);
 | |
|     prFilesStyle[vFlags][_criteria]->flags = vFlags;
 | |
|   }
 | |
| 
 | |
|   // todo : to refactor this fucking function
 | |
|   bool IGFD::FilterManager::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont **vOutFont)
 | |
|   {
 | |
|     if (vOutColor)
 | |
|     {
 | |
|       if (!prFilesStyle.empty())
 | |
|       {
 | |
|         if (prFilesStyle.find(vFlags) != prFilesStyle.end()) // found
 | |
|         {
 | |
|           if (vFlags & IGFD_FileStyleByContainedInFullName)
 | |
|           {
 | |
|             // search for vCriteria who are containing the criteria
 | |
|             for (const auto& _file : prFilesStyle.at(vFlags))
 | |
|             {
 | |
|               if (vCriteria.find(_file.first) != std::string::npos)
 | |
|               {
 | |
|                 if (_file.second.use_count())
 | |
|                 {
 | |
|                   *vOutColor = _file.second->color;
 | |
|                   if (vOutIcon)
 | |
|                     *vOutIcon = _file.second->icon;
 | |
|                   if (vOutFont)
 | |
|                     *vOutFont = _file.second->font;
 | |
|                   return true;
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|           else
 | |
|           {
 | |
|             if (prFilesStyle.at(vFlags).find(vCriteria) != prFilesStyle.at(vFlags).end()) // found
 | |
|             {
 | |
|               *vOutColor = prFilesStyle[vFlags][vCriteria]->color;
 | |
|               if (vOutIcon)
 | |
|                 *vOutIcon = prFilesStyle[vFlags][vCriteria]->icon;
 | |
|               if (vOutFont)
 | |
|                 *vOutFont = prFilesStyle[vFlags][vCriteria]->font;
 | |
|               return true;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|           // search for flag composition
 | |
|           for (const auto& _flag : prFilesStyle)
 | |
|           {
 | |
|             if (_flag.first & vFlags)
 | |
|             {
 | |
|               if (_flag.first & IGFD_FileStyleByContainedInFullName)
 | |
|               {
 | |
|                 // search for vCriteria who are containing the criteria
 | |
|                 for (const auto& _file : prFilesStyle.at(_flag.first))
 | |
|                 {
 | |
|                   if (vCriteria.find(_file.first) != std::string::npos)
 | |
|                   {
 | |
|                     if (_file.second.use_count())
 | |
|                     {
 | |
|                       *vOutColor = _file.second->color;
 | |
|                       if (vOutIcon)
 | |
|                         *vOutIcon = _file.second->icon;
 | |
|                       if (vOutFont)
 | |
|                         *vOutFont = _file.second->font;
 | |
|                       return true;
 | |
|                     }
 | |
|                   }
 | |
|                 }
 | |
|               }
 | |
|               else
 | |
|               {
 | |
|                 if (prFilesStyle.at(_flag.first).find(vCriteria) != prFilesStyle.at(_flag.first).end()) // found
 | |
|                 {
 | |
|                   *vOutColor = prFilesStyle[_flag.first][vCriteria]->color;
 | |
|                   if (vOutIcon)
 | |
|                     *vOutIcon = prFilesStyle[_flag.first][vCriteria]->icon;
 | |
|                   if (vOutFont)
 | |
|                     *vOutFont = prFilesStyle[_flag.first][vCriteria]->font;
 | |
|                   return true;
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FilterManager::ClearFilesStyle()
 | |
|   {
 | |
|     prFilesStyle.clear();
 | |
|   }
 | |
|     
 | |
|   bool IGFD::FilterManager::IsCoveredByFilters(const std::string& vTag) const
 | |
|   {
 | |
|     if (!puDLGFilters.empty() && !prSelectedFilter.empty())
 | |
|     {
 | |
|       // check if current file extention is covered by current filter
 | |
|       // we do that here, for avoid doing that during filelist display
 | |
|       // for better fps
 | |
|       if (prSelectedFilter.exist(vTag) || prSelectedFilter.firstFilter == ".*")
 | |
|       {
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FilterManager::DrawFilterComboBox(FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     // combobox of filters
 | |
|     if (!puDLGFilters.empty())
 | |
|     {
 | |
|       ImGui::SameLine();
 | |
| 
 | |
|       bool needToApllyNewFilter = false;
 | |
| 
 | |
|       ImGui::PushItemWidth(FILTER_COMBO_WIDTH*FileDialog::Instance()->DpiScale);
 | |
|       if (ImGui::BeginCombo("##Filters", prSelectedFilter.filter.c_str(), ImGuiComboFlags_None))
 | |
|       {
 | |
|         intptr_t i = 0;
 | |
|         for (const auto& filter : prParsedFilters)
 | |
|         {
 | |
|           const bool item_selected = (filter.filter == prSelectedFilter.filter);
 | |
|           ImGui::PushID((void*)(intptr_t)i++);
 | |
|           if (ImGui::Selectable(filter.filter.c_str(), item_selected))
 | |
|           {
 | |
|             prSelectedFilter = filter;
 | |
|             needToApllyNewFilter = true;
 | |
|           }
 | |
|           ImGui::PopID();
 | |
|         }
 | |
| 
 | |
|         ImGui::EndCombo();
 | |
|       }
 | |
|       ImGui::PopItemWidth();
 | |
| 
 | |
|       if (needToApllyNewFilter)
 | |
|       {
 | |
|         vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal);
 | |
|       }
 | |
| 
 | |
|       return needToApllyNewFilter;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   IGFD::FilterManager::FilterInfos IGFD::FilterManager::GetSelectedFilter()
 | |
|   {
 | |
|     return prSelectedFilter;
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FilterManager::ReplaceExtentionWithCurrentFilter(const std::string& vFile) const
 | |
|   {
 | |
|     auto result = vFile;
 | |
| 
 | |
|     if (!result.empty())
 | |
|     {
 | |
|       // if not a collection we can replace the filter by the extention we want
 | |
|       if (prSelectedFilter.collectionfilters.empty())
 | |
|       {
 | |
|         size_t lastPoint = vFile.find_last_of('.');
 | |
|         if (lastPoint != std::string::npos)
 | |
|         {
 | |
|           result = result.substr(0, lastPoint);
 | |
|         }
 | |
| 
 | |
|         result += prSelectedFilter.filter;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return result;
 | |
|   }
 | |
|   
 | |
|   void IGFD::FilterManager::SetDefaultFilterIfNotDefined()
 | |
|   {
 | |
|     if (prSelectedFilter.empty() && // no filter selected
 | |
|       !prParsedFilters.empty()) // filter exist
 | |
|       prSelectedFilter = *prParsedFilters.begin(); // we take the first filter
 | |
|   }
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// FILE MANAGER ///////////////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   IGFD::FileManager::FileManager()
 | |
|   {
 | |
|     puFsRoot = std::string(1u, PATH_SEP);
 | |
|     puSortingDirection[0]=true;
 | |
|                 for (int i=1; i<4; i++) {
 | |
|                   puSortingDirection[i]=false;
 | |
|                 }
 | |
|     invalidFile.isValid=false;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::OpenCurrentPath(const FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     puShowDrives = false;
 | |
|     ClearComposer();
 | |
|     ClearFileLists();
 | |
|     if (puDLGDirectoryMode) // directory mode
 | |
|       SetDefaultFileName(".");
 | |
|     else
 | |
|       SetDefaultFileName("");
 | |
|     logV("IGFD: OpenCurrentPath()");
 | |
|     ScanDir(vFileDialogInternal, GetCurrentPath());
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::SortFields(const FileDialogInternal& vFileDialogInternal, const SortingFieldEnum& vSortingField, const bool vCanChangeOrder)
 | |
|   {
 | |
|     logV("IGFD: SortFields()");
 | |
|     if (vSortingField != SortingFieldEnum::FIELD_NONE)
 | |
|     {
 | |
|       puHeaderFileName = FileDialog::Instance()->tableHeaderFileNameString;
 | |
|       puHeaderFileType = FileDialog::Instance()->tableHeaderFileTypeString;
 | |
|       puHeaderFileSize = FileDialog::Instance()->tableHeaderFileSizeString;
 | |
|       puHeaderFileDate = FileDialog::Instance()->tableHeaderFileDateString;
 | |
| #ifdef USE_THUMBNAILS
 | |
|       puHeaderFileThumbnails = tableHeaderFileThumbnailsString;
 | |
| #endif // #ifdef USE_THUMBNAILS
 | |
|     } else {
 | |
|       logV("IGFD: sorting by NONE!");
 | |
|     }
 | |
| 
 | |
|     if (prFileList.empty()) {
 | |
|       logV("IGFD: with an empty file list?");
 | |
|     }
 | |
| 
 | |
|     if (vSortingField == SortingFieldEnum::FIELD_FILENAME)
 | |
|     {
 | |
|       logV("IGFD: sorting by name");
 | |
|       if (vCanChangeOrder && puSortingField == vSortingField) {
 | |
|         //printf("Change the sorting\n");
 | |
|         puSortingDirection[0] = !puSortingDirection[0];
 | |
|       }
 | |
| 
 | |
|       if (puSortingDirection[0])
 | |
|       {
 | |
| #ifdef USE_CUSTOM_SORTING_ICON
 | |
|         puHeaderFileName = tableHeaderDescendingIcon + puHeaderFileName;
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|         std::sort(prFileList.begin(), prFileList.end(),
 | |
|           [](const FileInfos& a, const FileInfos& b) -> bool
 | |
|           {
 | |
|             // this code fail in c:\\Users with the link "All users". got a invalid comparator
 | |
|             /*
 | |
|             // use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571
 | |
|             // strict ordering for file/directory types beginning in '.'
 | |
|             // common on Linux platforms
 | |
|             if (a.fileNameExt[0] == '.' && b.fileNameExt[0] != '.')
 | |
|               return false;
 | |
|             if (a.fileNameExt[0] != '.' && b.fileNameExt[0] == '.')
 | |
|               return true;
 | |
|             if (a.fileNameExt[0] == '.' && b.fileNameExt[0] == '.')
 | |
|             {
 | |
|               return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) < 0); // sort in insensitive case
 | |
|             }
 | |
|             */
 | |
|             if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
 | |
|             return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) < 0); // sort in insensitive case
 | |
|           });
 | |
|       }
 | |
|       else
 | |
|       {
 | |
| #ifdef USE_CUSTOM_SORTING_ICON
 | |
|         puHeaderFileName = tableHeaderAscendingIcon + puHeaderFileName;
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|         std::sort(prFileList.begin(), prFileList.end(),
 | |
|           [](const FileInfos& a, const FileInfos& b) -> bool
 | |
|           {
 | |
|             // this code fail in c:\\Users with the link "All users". got a invalid comparator
 | |
|             /*
 | |
|             // use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571
 | |
|             // strict ordering for file/directory types beginning in '.'
 | |
|             // common on Linux platforms
 | |
|             if (a.fileNameExt[0] == '.' && b.fileNameExt[0] != '.')
 | |
|               return false;
 | |
|             if (a.fileNameExt[0] != '.' && b.fileNameExt[0] == '.')
 | |
|               return true;
 | |
|             if (a.fileNameExt[0] == '.' && b.fileNameExt[0] == '.')
 | |
|             {
 | |
|               return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) > 0); // sort in insensitive case
 | |
|             }
 | |
|             */
 | |
|             return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) > 0); // sort in insensitive case
 | |
|           });
 | |
|       }
 | |
|     }
 | |
|     else if (vSortingField == SortingFieldEnum::FIELD_TYPE)
 | |
|     {
 | |
|       logV("IGFD: sorting by type");
 | |
|       if (vCanChangeOrder && puSortingField == vSortingField)
 | |
|         puSortingDirection[1] = !puSortingDirection[1];
 | |
| 
 | |
|       if (puSortingDirection[1])
 | |
|       {
 | |
| #ifdef USE_CUSTOM_SORTING_ICON
 | |
|         puHeaderFileType = tableHeaderDescendingIcon + puHeaderFileType;
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|         std::sort(prFileList.begin(), prFileList.end(),
 | |
|           [](const FileInfos& a, const FileInfos& b) -> bool
 | |
|           {
 | |
|             if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
 | |
|             return (a.fileExt < b.fileExt); // else
 | |
|           });
 | |
|       }
 | |
|       else
 | |
|       {
 | |
| #ifdef USE_CUSTOM_SORTING_ICON
 | |
|         puHeaderFileType = tableHeaderAscendingIcon + puHeaderFileType;
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|         std::sort(prFileList.begin(), prFileList.end(),
 | |
|           [](const FileInfos& a, const FileInfos& b) -> bool
 | |
|           {
 | |
|             if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
 | |
|             return (a.fileExt > b.fileExt); // else
 | |
|           });
 | |
|       }
 | |
|     }
 | |
|     else if (vSortingField == SortingFieldEnum::FIELD_SIZE)
 | |
|     {
 | |
|       logV("IGFD: sorting by size");
 | |
|       if (vCanChangeOrder && puSortingField == vSortingField)
 | |
|         puSortingDirection[2] = !puSortingDirection[2];
 | |
| 
 | |
|       if (puSortingDirection[2])
 | |
|       {
 | |
| #ifdef USE_CUSTOM_SORTING_ICON
 | |
|         puHeaderFileSize = tableHeaderDescendingIcon + puHeaderFileSize;
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|         std::sort(prFileList.begin(), prFileList.end(),
 | |
|           [](const FileInfos& a, const FileInfos& b) -> bool
 | |
|           {
 | |
|             if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
 | |
|             return (a.fileSize < b.fileSize); // else
 | |
|           });
 | |
|       }
 | |
|       else
 | |
|       {
 | |
| #ifdef USE_CUSTOM_SORTING_ICON
 | |
|         puHeaderFileSize = tableHeaderAscendingIcon + puHeaderFileSize;
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|         std::sort(prFileList.begin(), prFileList.end(),
 | |
|           [](const FileInfos& a, const FileInfos& b) -> bool
 | |
|           {
 | |
|             if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
 | |
|             return (a.fileSize > b.fileSize); // else
 | |
|           });
 | |
|       }
 | |
|     }
 | |
|     else if (vSortingField == SortingFieldEnum::FIELD_DATE)
 | |
|     {
 | |
|       logV("IGFD: sorting by date");
 | |
|       if (vCanChangeOrder && puSortingField == vSortingField)
 | |
|         puSortingDirection[3] = !puSortingDirection[3];
 | |
| 
 | |
|       if (puSortingDirection[3])
 | |
|       {
 | |
| #ifdef USE_CUSTOM_SORTING_ICON
 | |
|         puHeaderFileDate = tableHeaderDescendingIcon + puHeaderFileDate;
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|         std::sort(prFileList.begin(), prFileList.end(),
 | |
|           [](const FileInfos& a, const FileInfos& b) -> bool
 | |
|           {
 | |
|             if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
 | |
|             return (a.fileModifDate < b.fileModifDate); // else
 | |
|           });
 | |
|       }
 | |
|       else
 | |
|       {
 | |
| #ifdef USE_CUSTOM_SORTING_ICON
 | |
|         puHeaderFileDate = tableHeaderAscendingIcon + puHeaderFileDate;
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|         std::sort(prFileList.begin(), prFileList.end(),
 | |
|           [](const FileInfos& a, const FileInfos& b) -> bool
 | |
|           {
 | |
|             if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
 | |
|             return (a.fileModifDate > b.fileModifDate); // else
 | |
|           });
 | |
|       }
 | |
|     }
 | |
| #ifdef USE_THUMBNAILS
 | |
|     else if (vSortingField == SortingFieldEnum::FIELD_THUMBNAILS)
 | |
|     {
 | |
|       if (vCanChangeOrder && puSortingField == vSortingField)
 | |
|         puSortingDirection[4] = !puSortingDirection[4];
 | |
| 
 | |
|       // we will compare thumbnails by :
 | |
|       // 1) width 
 | |
|       // 2) height
 | |
| 
 | |
|       if (puSortingDirection[4])
 | |
|       {
 | |
| #ifdef USE_CUSTOM_SORTING_ICON
 | |
|         puHeaderFileThumbnails = tableHeaderDescendingIcon + puHeaderFileThumbnails;
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|         std::sort(prFileList.begin(), prFileList.end(),
 | |
|           [](const FileInfos& a, const FileInfos& b) -> bool
 | |
|           {
 | |
|             if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
 | |
|             if (a.thumbnailInfo.textureWidth == b.thumbnailInfo.textureWidth)
 | |
|               return (a.thumbnailInfo.textureHeight < b.thumbnailInfo.textureHeight);
 | |
|             return (a.thumbnailInfo.textureWidth < b.thumbnailInfo.textureWidth);
 | |
|           });
 | |
|       }
 | |
| 
 | |
|       else
 | |
|       {
 | |
| #ifdef USE_CUSTOM_SORTING_ICON
 | |
|         puHeaderFileThumbnails = tableHeaderAscendingIcon + puHeaderFileThumbnails;
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|         std::sort(prFileList.begin(), prFileList.end(),
 | |
|           [](const FileInfos& a, const FileInfos& b) -> bool
 | |
|           {
 | |
|             if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
 | |
|             if (a.thumbnailInfo.textureWidth == b.thumbnailInfo.textureWidth)
 | |
|               return (a.thumbnailInfo.textureHeight > b.thumbnailInfo.textureHeight);
 | |
|             return (a.thumbnailInfo.textureWidth > b.thumbnailInfo.textureWidth);
 | |
|           });
 | |
|       }
 | |
|     }
 | |
| #endif // USE_THUMBNAILS
 | |
| 
 | |
|     if (vSortingField != SortingFieldEnum::FIELD_NONE)
 | |
|     {
 | |
|       puSortingField = vSortingField;
 | |
|     }
 | |
| 
 | |
|     logV("IGFD: applying filtering on file list");
 | |
| 
 | |
|     ApplyFilteringOnFileList(vFileDialogInternal);
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::ClearFileLists()
 | |
|   {
 | |
|     prFilteredFileList.clear();
 | |
|     prFileList.clear();
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileManager::prOptimizeFilenameForSearchOperations(const std::string& vFileNameExt)
 | |
|   {
 | |
|     auto fileNameExt = vFileNameExt;
 | |
|     // convert to lower case
 | |
|     for (char& c : fileNameExt)
 | |
|       c = (char)std::tolower(c);
 | |
|     return fileNameExt;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, char vFileType, void* ent)
 | |
|   {
 | |
|     FileInfos infos;
 | |
| 
 | |
| #ifdef _WIN32
 | |
|     struct dirent* dent=(struct dirent*)ent;
 | |
| #endif
 | |
| 
 | |
|     infos.filePath = vPath;
 | |
|     infos.fileNameExt = vFileName;
 | |
|     infos.fileNameExt_optimized = prOptimizeFilenameForSearchOperations(infos.fileNameExt);
 | |
|     infos.fileType = vFileType;
 | |
| 
 | |
|     if (infos.fileNameExt.empty() || ((infos.fileNameExt == "." || infos.fileNameExt == "..") && !vFileDialogInternal.puFilterManager.puDLGFilters.empty())) return; // filename empty or filename is the current dir '.' //-V807
 | |
|     if (infos.fileNameExt != ".." && (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos.fileNameExt[0] == '.') // dont show hidden files
 | |
|       if (!vFileDialogInternal.puFilterManager.puDLGFilters.empty() || (vFileDialogInternal.puFilterManager.puDLGFilters.empty() && infos.fileNameExt != ".")) // except "." if in directory mode //-V728
 | |
|         return;
 | |
| 
 | |
|     if (infos.fileType == 'f' ||
 | |
|       infos.fileType == 'l') // link can have the same extention of a file
 | |
|     {
 | |
|       size_t lpt = infos.fileNameExt.find_last_of('.');
 | |
|       if (lpt != std::string::npos)
 | |
|       {
 | |
|         infos.fileExt = infos.fileNameExt.substr(lpt);
 | |
|       }
 | |
| 
 | |
|       for (char& i: infos.fileExt) {
 | |
|         if (i>='A' && i<='Z') i+='a'-'A';
 | |
|       }
 | |
| 
 | |
|       if (!vFileDialogInternal.puFilterManager.IsCoveredByFilters(infos.fileExt))
 | |
|       {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
| 
 | |
| #ifdef _WIN32
 | |
|     SYSTEMTIME systemTime;
 | |
|     SYSTEMTIME localTime;
 | |
|     char timebuf[100];
 | |
| 
 | |
|     infos.fileSize=dent->dwin_size;
 | |
|     if (FileTimeToSystemTime(&dent->dwin_mtime,&systemTime)==TRUE) {
 | |
|       if (SystemTimeToTzSpecificLocalTime(NULL,&systemTime,&localTime)==TRUE) {
 | |
|         snprintf(timebuf,99,"%d/%.2d/%.2d %.2d:%.2d",localTime.wYear,localTime.wMonth,localTime.wDay,localTime.wHour,localTime.wMinute);
 | |
|       } else {
 | |
|         snprintf(timebuf,99,"%d/%.2d/%.2d %.2d:%.2d",systemTime.wYear,systemTime.wMonth,systemTime.wDay,systemTime.wHour,systemTime.wMinute);
 | |
|       }
 | |
|       infos.fileModifDate=timebuf;
 | |
|     } else {
 | |
|       infos.fileModifDate="???";
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     vFileDialogInternal.puFilterManager.prFillFileStyle(infos);
 | |
| 
 | |
|     prCompleteFileInfos(infos);
 | |
|     prFileList.push_back(infos);
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::ScanDir(const FileDialogInternal& vFileDialogInternal, const std::string& vPath)
 | |
|   {
 | |
|     std::string  path = vPath;
 | |
|     logV("IGFD: ScanDir(%s)",vPath);
 | |
| 
 | |
|     if (prCurrentPathDecomposition.empty())
 | |
|     {
 | |
|       logV("IGFD: the current path decomposition is empty. setting.");
 | |
|       SetCurrentDir(path);
 | |
|     }
 | |
| 
 | |
|     if (!prCurrentPathDecomposition.empty())
 | |
|     {
 | |
|       logV("IGFD: the current path decomposition is not empty. trying.");
 | |
| #ifdef WIN32
 | |
|       if (path == puFsRoot)
 | |
|         path += std::string(1u, PATH_SEP);
 | |
| #endif // WIN32
 | |
| 
 | |
|       ClearFileLists();
 | |
| 
 | |
|       struct dirent** files = nullptr;
 | |
|       int n = scandir(path.c_str(), &files, nullptr, inAlphaSort);
 | |
|       logV("IGFD: %d entries in directory",n);
 | |
|       if (n>0)
 | |
|       {
 | |
|         int i;
 | |
| 
 | |
|         for (i = 0; i < n; i++)
 | |
|         {
 | |
|           struct dirent* ent = files[i];
 | |
|           std::string where = path + std::string(PATH_SEP_STR) + std::string(ent->d_name);
 | |
|           char fileType = 'f';
 | |
| #if defined(HAVE_DIRENT_TYPE) || defined(_WIN32)
 | |
|           if (ent->d_type != DT_UNKNOWN)
 | |
|           {
 | |
|             switch (ent->d_type)
 | |
|             {
 | |
|             case DT_REG:
 | |
|               fileType = 'f'; break;
 | |
|             case DT_DIR:
 | |
|               fileType = 'd'; break;
 | |
|             case DT_LNK: {
 | |
| #ifdef _WIN32
 | |
|               fileType = 'f';
 | |
| #else
 | |
|               DIR* dirTest = opendir(where.c_str());
 | |
|               if (dirTest == NULL)
 | |
|               {
 | |
|                 if (errno == ENOTDIR)
 | |
|                 {
 | |
|                   fileType = 'f';
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                   // why does 'l' make it crash?
 | |
|                   fileType = 'f';
 | |
|                 }
 | |
|               }
 | |
|               else
 | |
|               {
 | |
|                 fileType = 'd';
 | |
|                 closedir(dirTest);
 | |
|               }
 | |
| #endif
 | |
|               break;
 | |
|             }
 | |
|             default:
 | |
|               fileType = 'f'; break;
 | |
|             }
 | |
|           }
 | |
|           else
 | |
| #endif // HAVE_DIRENT_TYPE
 | |
|           {
 | |
| #ifndef _WIN32
 | |
|             struct stat filestat;
 | |
|             if (stat(where.c_str(), &filestat) == 0)
 | |
|             {
 | |
|               if (S_ISDIR(filestat.st_mode))
 | |
|               {
 | |
|                 fileType = 'd';
 | |
|               }
 | |
|               else
 | |
|               {
 | |
|                 fileType = 'f';
 | |
|               }
 | |
|             }
 | |
| #endif
 | |
|           }
 | |
| 
 | |
|           auto fileNameExt = ent->d_name;
 | |
| 
 | |
|           AddFile(vFileDialogInternal, path, fileNameExt, fileType, ent);
 | |
|         }
 | |
| 
 | |
|         for (i = 0; i < n; i++)
 | |
|         {
 | |
|           free(files[i]);
 | |
|         }
 | |
| 
 | |
|         free(files);
 | |
|       } else {
 | |
|         logV("IGFD: it's empty");
 | |
|       }
 | |
| 
 | |
|       logV("IGFD: sorting fields...");
 | |
|       SortFields(vFileDialogInternal, puSortingField, false);
 | |
|     } else {
 | |
|       logE("IGFD: current path decomposition is empty!");
 | |
|     }
 | |
| 
 | |
|     fileListActuallyEmpty=prFileList.empty();
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileManager::GetDrives()
 | |
|   {
 | |
|     auto drives = IGFD::Utils::GetDrivesList();
 | |
|     if (!drives.empty())
 | |
|     {
 | |
|       prCurrentPath.clear();
 | |
|       prCurrentPathDecomposition.clear();
 | |
|       ClearFileLists();
 | |
|       for (auto& drive : drives)
 | |
|       {
 | |
|         FileInfos info;
 | |
|         info.fileNameExt = drive;
 | |
|         info.fileNameExt_optimized = prOptimizeFilenameForSearchOperations(drive);
 | |
|         info.fileType = 'd';
 | |
| 
 | |
|         if (!info.fileNameExt.empty())
 | |
|         {
 | |
|           prFileList.push_back(info);
 | |
|         }
 | |
|       }
 | |
|       puShowDrives = true;
 | |
|       return true;
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileManager::IsComposerEmpty()
 | |
|   {
 | |
|     return prCurrentPathDecomposition.empty();
 | |
|   }
 | |
|   
 | |
|   size_t IGFD::FileManager::GetComposerSize()
 | |
|   {
 | |
|     return prCurrentPathDecomposition.size();
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileManager::IsFileListEmpty()
 | |
|   {
 | |
|     return prFileList.empty();
 | |
|   }
 | |
| 
 | |
|   size_t IGFD::FileManager::GetFullFileListSize()
 | |
|   {
 | |
|     return prFileList.size();
 | |
|   }
 | |
| 
 | |
|   const FileInfos& IGFD::FileManager::GetFullFileAt(size_t vIdx)
 | |
|   {
 | |
|     if (vIdx < prFileList.size())
 | |
|       return prFileList[vIdx];
 | |
|     return invalidFile;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileManager::IsFilteredListEmpty()
 | |
|   {
 | |
|     return prFilteredFileList.empty();
 | |
|   }
 | |
| 
 | |
|   size_t IGFD::FileManager::GetFilteredListSize()
 | |
|   {
 | |
|     return prFilteredFileList.size();
 | |
|   }
 | |
| 
 | |
|   const FileInfos& IGFD::FileManager::GetFilteredFileAt(size_t vIdx)
 | |
|   {
 | |
|     if (vIdx < prFilteredFileList.size())
 | |
|       return prFilteredFileList[vIdx];
 | |
|     return invalidFile;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileManager::IsFileNameSelected(const std::string& vFileName)
 | |
|   {
 | |
|     return prSelectedFileNames.find(vFileName) != prSelectedFileNames.end();
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileManager::GetBack()
 | |
|   {
 | |
|     return prCurrentPathDecomposition.back();
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::ClearComposer()
 | |
|   {
 | |
|     prCurrentPathDecomposition.clear();
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::ClearAll()
 | |
|   {
 | |
|     ClearComposer();
 | |
|     ClearFileLists();
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     prFilteredFileList.clear();
 | |
|     for (const FileInfos& file : prFileList)
 | |
|     {
 | |
|       bool show = true;
 | |
|       if (!file.IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag))  // if search tag
 | |
|         show = false;
 | |
|       if (puDLGDirectoryMode && file.fileType != 'd') // directory mode
 | |
|         show = false;
 | |
|       if (show)
 | |
|         prFilteredFileList.push_back(file);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileManager::prRoundNumber(double vvalue, int n)
 | |
|   {
 | |
|     std::stringstream tmp;
 | |
|     tmp << std::setprecision(n) << std::fixed << vvalue;
 | |
|     return tmp.str();
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileManager::prFormatFileSize(size_t vByteSize)
 | |
|   {
 | |
|     if (vByteSize != 0)
 | |
|     {
 | |
|       static double lo = 1024.0;
 | |
|       static double ko = 1024.0 * 1024.0;
 | |
|       static double mo = 1024.0 * 1024.0 * 1024.0;
 | |
| 
 | |
|       auto v = (double)vByteSize;
 | |
| 
 | |
|       if (v < lo)
 | |
|         return prRoundNumber(v, 0); // octet
 | |
|       else if (v < ko)
 | |
|         return prRoundNumber(v / lo, 2) + "K"; // ko
 | |
|       else  if (v < mo)
 | |
|         return prRoundNumber(v / ko, 2) + "M"; // Mo 
 | |
|       else
 | |
|         return prRoundNumber(v / mo, 2) + "G"; // Go 
 | |
|     }
 | |
| 
 | |
|     return "";
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::prCompleteFileInfos(FileInfos& vInfos)
 | |
|   {
 | |
|     if (vInfos.fileNameExt != "." &&
 | |
|       vInfos.fileNameExt != "..")
 | |
|     {
 | |
|       // _stat struct :
 | |
|       //dev_t     st_dev;     /* ID of device containing file */
 | |
|       //ino_t     st_ino;     /* inode number */
 | |
|       //mode_t    st_mode;    /* protection */
 | |
|       //nlink_t   st_nlink;   /* number of hard links */
 | |
|       //uid_t     st_uid;     /* user ID of owner */
 | |
|       //gid_t     st_gid;     /* group ID of owner */
 | |
|       //dev_t     st_rdev;    /* device ID (if special file) */
 | |
|       //off_t     st_size;    /* total size, in bytes */
 | |
|       //blksize_t st_blksize; /* blocksize for file system I/O */
 | |
|       //blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
 | |
|       //time_t    st_atime;   /* time of last access - not sure out of ntfs */
 | |
|       //time_t    st_mtime;   /* time of last modification - not sure out of ntfs */
 | |
|       //time_t    st_ctime;   /* time of last status change - not sure out of ntfs */
 | |
| 
 | |
| #ifdef _WIN32
 | |
|       if (vInfos.fileType != 'd')
 | |
|       {
 | |
|         vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize);
 | |
|       }
 | |
| #else
 | |
|       std::string fpn;
 | |
| 
 | |
|       if (vInfos.fileType == 'f' || vInfos.fileType == 'l' || vInfos.fileType == 'd') // file
 | |
|         fpn = vInfos.filePath + std::string(1u, PATH_SEP) + vInfos.fileNameExt;
 | |
| 
 | |
|       struct stat statInfos = {};
 | |
|       char timebuf[100];
 | |
|       int result = stat(fpn.c_str(), &statInfos);
 | |
|       if (result!=-1)
 | |
|       {
 | |
|         if (vInfos.fileType != 'd')
 | |
|         {
 | |
|           vInfos.fileSize = (size_t)statInfos.st_size;
 | |
|           vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize);
 | |
|         }
 | |
| 
 | |
|         size_t len = 0;
 | |
| #ifdef MSVC
 | |
|         struct tm _tm;
 | |
|         errno_t err = localtime_s(&_tm, &statInfos.st_mtime);
 | |
|         if (!err) len = strftime(timebuf, 99, FileDialog::Instance()->DateTimeFormat, &_tm);
 | |
| #else // MSVC
 | |
|         struct tm* _tm = localtime(&statInfos.st_mtime);
 | |
|         if (_tm) len = strftime(timebuf, 99, FileDialog::Instance()->DateTimeFormat, _tm);
 | |
| #endif // MSVC
 | |
|         if (len)
 | |
|         {
 | |
|           vInfos.fileModifDate = std::string(timebuf, len);
 | |
|         }
 | |
|       } else {
 | |
|         vInfos.fileSize=0;
 | |
|         vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize);
 | |
|         vInfos.fileModifDate="???";
 | |
|       }
 | |
| #endif
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::prRemoveFileNameInSelection(const std::string& vFileName)
 | |
|   {
 | |
|     prSelectedFileNames.erase(vFileName);
 | |
| 
 | |
|     if (prSelectedFileNames.size() == 1)
 | |
|     {
 | |
|       snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str());
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", prSelectedFileNames.size());
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   void IGFD::FileManager::prAddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName)
 | |
|   {
 | |
|     prSelectedFileNames.emplace(vFileName);
 | |
| 
 | |
|     if (prSelectedFileNames.size() == 1)
 | |
|     {
 | |
|       snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str());
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", prSelectedFileNames.size());
 | |
|     }
 | |
| 
 | |
|     if (vSetLastSelectionFileName)
 | |
|       prLastSelectedFileName = vFileName;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::SetCurrentDir(const std::string& vPath)
 | |
|   {
 | |
|     std::string path = vPath;
 | |
| #ifdef WIN32
 | |
|     if (puFsRoot == path)
 | |
|       path += std::string(1u, PATH_SEP);
 | |
| #endif // WIN32
 | |
|     
 | |
|     DIR* dir = opendir(path.c_str());
 | |
|     if (dir == nullptr)
 | |
|     {
 | |
|       path = ".";
 | |
|       dir = opendir(path.c_str());
 | |
|     }
 | |
| 
 | |
|     if (dir != nullptr)
 | |
|     {
 | |
| #ifdef WIN32
 | |
|       DWORD numchar = 0;
 | |
|       //      numchar = GetFullPathNameA(path.c_str(), PATH_MAX, real_path, nullptr);
 | |
|       std::wstring wpath = IGFD::Utils::string_to_wstring(path);
 | |
|       numchar = GetFullPathNameW(wpath.c_str(), 0, nullptr, nullptr);
 | |
|       std::wstring fpath(numchar, 0);
 | |
|       GetFullPathNameW(wpath.c_str(), numchar, (wchar_t*)fpath.data(), nullptr);
 | |
|       std::string real_path = IGFD::Utils::wstring_to_string(fpath);
 | |
|       if (real_path.back() == '\0') // for fix issue we can have with std::string concatenation.. if there is a \0 at end
 | |
|         real_path = real_path.substr(0, real_path.size() - 1U);
 | |
|       if (!real_path.empty())
 | |
| #elif defined(UNIX) // UNIX is LINUX or APPLE
 | |
|       char real_path[PATH_MAX]; 
 | |
|       char* numchar = realpath(path.c_str(), real_path);
 | |
|       if (numchar != nullptr)
 | |
| #endif // WIN32
 | |
|       {
 | |
|         prCurrentPath = std::move(real_path);
 | |
|         if (prCurrentPath[prCurrentPath.size() - 1] == PATH_SEP)
 | |
|         {
 | |
|           prCurrentPath = prCurrentPath.substr(0, prCurrentPath.size() - 1);
 | |
|         }
 | |
|         IGFD::Utils::SetBuffer(puInputPathBuffer, MAX_PATH_BUFFER_SIZE, prCurrentPath);
 | |
|         prCurrentPathDecomposition = IGFD::Utils::SplitStringToVector(prCurrentPath, PATH_SEP, false);
 | |
| #ifdef UNIX // UNIX is LINUX or APPLE
 | |
|         prCurrentPathDecomposition.insert(prCurrentPathDecomposition.begin(), std::string(1u, PATH_SEP));
 | |
| #endif // UNIX
 | |
|         if (!prCurrentPathDecomposition.empty())
 | |
|         {
 | |
| #ifdef WIN32
 | |
|           puFsRoot = prCurrentPathDecomposition[0];
 | |
| #endif // WIN32
 | |
|         }
 | |
|       }
 | |
|       closedir(dir);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileManager::CreateDir(const std::string& vPath)
 | |
|   {
 | |
|     bool res = false;
 | |
| 
 | |
|     if (!vPath.empty())
 | |
|     {
 | |
|       std::string path = prCurrentPath + std::string(1u, PATH_SEP) + vPath;
 | |
| 
 | |
|       res = IGFD::Utils::CreateDirectoryIfNotExist(path);
 | |
|     }
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::ComposeNewPath(std::vector<std::string>::iterator vIter)
 | |
|   {
 | |
|     std::string res;
 | |
| 
 | |
|     while (true)
 | |
|     {
 | |
|       if (!res.empty())
 | |
|       {
 | |
| #ifdef WIN32
 | |
|         res = *vIter + std::string(1u, PATH_SEP) + res;
 | |
| #elif defined(UNIX) // UNIX is LINUX or APPLE
 | |
|         if (*vIter == puFsRoot)
 | |
|           res = *vIter + res;
 | |
|         else
 | |
|           res = *vIter + PATH_SEP + res;
 | |
| #endif // WIN32
 | |
|       }
 | |
|       else
 | |
|         res = *vIter;
 | |
| 
 | |
|       if (vIter == prCurrentPathDecomposition.begin())
 | |
|       {
 | |
| #if defined(UNIX) // UNIX is LINUX or APPLE
 | |
|         if (res[0] != PATH_SEP)
 | |
|           res = PATH_SEP + res;
 | |
| #endif // defined(UNIX)
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       --vIter;
 | |
|     }
 | |
| 
 | |
|     prCurrentPath = std::move(res);
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileManager::SetPathOnParentDirectoryIfAny()
 | |
|   {
 | |
|     if (prCurrentPathDecomposition.size() > 1)
 | |
|     {
 | |
|       ComposeNewPath(prCurrentPathDecomposition.end() - 2);
 | |
|       return true;
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileManager::GetCurrentPath()
 | |
|   {
 | |
|     if (prCurrentPath.empty())
 | |
|       prCurrentPath = ".";
 | |
|     return prCurrentPath;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::SetCurrentPath(const std::string& vCurrentPath)
 | |
|   {
 | |
|     if (vCurrentPath.empty())
 | |
|       prCurrentPath = ".";
 | |
|     else
 | |
|       prCurrentPath = vCurrentPath;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileManager::IsFileExist(const std::string& vFile, const std::string& vFileExt)
 | |
|   {
 | |
|     std::ifstream docFile(vFile, std::ios::in);
 | |
|     logV("IGFD: IsFileExist(%s)",vFile);
 | |
|     if (docFile.is_open())
 | |
|     {
 | |
|       docFile.close();
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     std::ifstream docFileE(vFile+vFileExt, std::ios::in);
 | |
|     logV("IGFD: IsFileExist(%s)",vFile+vFileExt);
 | |
|     if (docFileE.is_open())
 | |
|     {
 | |
|       docFileE.close();
 | |
|       return true;
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::SetDefaultFileName(const std::string& vFileName)
 | |
|   {
 | |
|     puDLGDefaultFileName = vFileName;
 | |
|     IGFD::Utils::SetBuffer(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFileName);
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileManager::SelectDirectory(const FileInfos& vInfos)
 | |
|   {
 | |
|     bool pathClick = false;
 | |
| 
 | |
|     if (vInfos.fileNameExt == "..")
 | |
|     {
 | |
|       pathClick = SetPathOnParentDirectoryIfAny();
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       std::string newPath;
 | |
| 
 | |
|       if (puShowDrives)
 | |
|       {
 | |
|         newPath = vInfos.fileNameExt + std::string(1u, PATH_SEP);
 | |
|       }
 | |
|       else
 | |
|       {
 | |
| #ifdef __linux__
 | |
|         if (puFsRoot == prCurrentPath)
 | |
|           newPath = prCurrentPath + vInfos.fileNameExt;
 | |
|         else
 | |
| #endif // __linux__
 | |
|           newPath = prCurrentPath + std::string(1u, PATH_SEP) + vInfos.fileNameExt;
 | |
|       }
 | |
| 
 | |
|       if (IGFD::Utils::IsDirectoryExist(newPath))
 | |
|       {
 | |
|         if (puShowDrives)
 | |
|         {
 | |
|           prCurrentPath = vInfos.fileNameExt;
 | |
|           puFsRoot = prCurrentPath;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|           prCurrentPath = newPath; //-V820
 | |
|         }
 | |
|         pathClick = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return pathClick;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::SelectFileName(const FileDialogInternal& vFileDialogInternal, const FileInfos& vInfos)
 | |
|   {
 | |
|     if (ImGui::GetIO().KeyCtrl)
 | |
|     {
 | |
|       if (puDLGcountSelectionMax == 0) // infinite selection
 | |
|       {
 | |
|         if (prSelectedFileNames.find(vInfos.fileNameExt) == prSelectedFileNames.end()) // not found +> add
 | |
|         {
 | |
|           prAddFileNameInSelection(vInfos.fileNameExt, true);
 | |
|         }
 | |
|         else // found +> remove
 | |
|         {
 | |
|           prRemoveFileNameInSelection(vInfos.fileNameExt);
 | |
|         }
 | |
|       }
 | |
|       else // selection limited by size
 | |
|       {
 | |
|         if (prSelectedFileNames.size() < puDLGcountSelectionMax)
 | |
|         {
 | |
|           if (prSelectedFileNames.find(vInfos.fileNameExt) == prSelectedFileNames.end()) // not found +> add
 | |
|           {
 | |
|             prAddFileNameInSelection(vInfos.fileNameExt, true);
 | |
|           }
 | |
|           else // found +> remove
 | |
|           {
 | |
|             prRemoveFileNameInSelection(vInfos.fileNameExt);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     else if (ImGui::GetIO().KeyShift)
 | |
|     {
 | |
|       if (puDLGcountSelectionMax != 1)
 | |
|       {
 | |
|         prSelectedFileNames.clear();
 | |
|         // we will iterate filelist and get the last selection after the start selection
 | |
|         bool startMultiSelection = false;
 | |
|         std::string fileNameToSelect = vInfos.fileNameExt;
 | |
|         std::string savedLastSelectedFileName; // for invert selection mode
 | |
|         for (const FileInfos& file : prFileList)
 | |
|         {
 | |
|           bool canTake = true;
 | |
|           if (!file.IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) canTake = false;
 | |
|           if (canTake) // if not filtered, we will take files who are filtered by the dialog
 | |
|           {
 | |
|             if (file.fileNameExt == prLastSelectedFileName)
 | |
|             {
 | |
|               startMultiSelection = true;
 | |
|               prAddFileNameInSelection(prLastSelectedFileName, false);
 | |
|             }
 | |
|             else if (startMultiSelection)
 | |
|             {
 | |
|               if (puDLGcountSelectionMax == 0) // infinite selection
 | |
|               {
 | |
|                 prAddFileNameInSelection(file.fileNameExt, false);
 | |
|               }
 | |
|               else // selection limited by size
 | |
|               {
 | |
|                 if (prSelectedFileNames.size() < puDLGcountSelectionMax)
 | |
|                 {
 | |
|                   prAddFileNameInSelection(file.fileNameExt, false);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                   startMultiSelection = false;
 | |
|                   if (!savedLastSelectedFileName.empty())
 | |
|                     prLastSelectedFileName = savedLastSelectedFileName;
 | |
|                   break;
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
| 
 | |
|             if (file.fileNameExt == fileNameToSelect)
 | |
|             {
 | |
|               if (!startMultiSelection) // we are before the last Selected FileName, so we must inverse
 | |
|               {
 | |
|                 savedLastSelectedFileName = prLastSelectedFileName;
 | |
|                 prLastSelectedFileName = fileNameToSelect;
 | |
|                 fileNameToSelect = savedLastSelectedFileName;
 | |
|                 startMultiSelection = true;
 | |
|                 prAddFileNameInSelection(prLastSelectedFileName, false);
 | |
|               }
 | |
|               else
 | |
|               {
 | |
|                 startMultiSelection = false;
 | |
|                 if (!savedLastSelectedFileName.empty())
 | |
|                   prLastSelectedFileName = savedLastSelectedFileName;
 | |
|                 break;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       prSelectedFileNames.clear();
 | |
|       IGFD::Utils::ResetBuffer(puFileNameBuffer);
 | |
|       prAddFileNameInSelection(vInfos.fileNameExt, true);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::DrawDirectoryCreation(const FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     if (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableCreateDirectoryButton)
 | |
|       return;
 | |
| 
 | |
|     if (IMGUI_BUTTON(createDirButtonString))
 | |
|     {
 | |
|       if (!prCreateDirectoryMode)
 | |
|       {
 | |
|         prCreateDirectoryMode = true;
 | |
|         IGFD::Utils::ResetBuffer(puDirectoryNameBuffer);
 | |
|       }
 | |
|     }
 | |
|     if (ImGui::IsItemHovered())
 | |
|       ImGui::SetTooltip("%s",FileDialog::Instance()->buttonCreateDirString);
 | |
| 
 | |
|     if (prCreateDirectoryMode)
 | |
|     {
 | |
|       ImGui::SameLine();
 | |
| 
 | |
|       ImGui::PushItemWidth(100.0f);
 | |
|       ImGui::InputText("##DirectoryFileName", puDirectoryNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
 | |
|       ImGui::PopItemWidth();
 | |
| 
 | |
|       ImGui::SameLine();
 | |
| 
 | |
|       if (IMGUI_BUTTON(FileDialog::Instance()->okButtonString))
 | |
|       {
 | |
|         std::string newDir = std::string(puDirectoryNameBuffer);
 | |
|         if (CreateDir(newDir))
 | |
|         {
 | |
|           SetCurrentPath(prCurrentPath + std::string(1u, PATH_SEP) + newDir);
 | |
|           OpenCurrentPath(vFileDialogInternal);
 | |
|         }
 | |
| 
 | |
|         prCreateDirectoryMode = false;
 | |
|       }
 | |
| 
 | |
|       ImGui::SameLine();
 | |
| 
 | |
|       if (IMGUI_BUTTON(FileDialog::Instance()->cancelButtonString))
 | |
|       {
 | |
|         prCreateDirectoryMode = false;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileManager::DrawPathComposer(const FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     if (IMGUI_BUTTON(homeButtonString))
 | |
|     {
 | |
|       SetCurrentPath(FileDialog::Instance()->homePath);
 | |
|       OpenCurrentPath(vFileDialogInternal);
 | |
|     }
 | |
|     if (ImGui::IsItemHovered())
 | |
|       ImGui::SetTooltip("%s",FileDialog::Instance()->buttonResetPathString);
 | |
| 
 | |
|     ImGui::SameLine();
 | |
|     if (IMGUI_BUTTON(parentDirString))
 | |
|     {
 | |
|       if (SetPathOnParentDirectoryIfAny()) {
 | |
|         OpenCurrentPath(vFileDialogInternal);
 | |
|       }
 | |
|     }
 | |
|     if (ImGui::IsItemHovered())
 | |
|       ImGui::SetTooltip("%s",FileDialog::Instance()->buttonParentDirString);
 | |
| 
 | |
| #ifdef WIN32
 | |
|     ImGui::SameLine();
 | |
| 
 | |
|     if (IMGUI_BUTTON(drivesButtonString))
 | |
|     {
 | |
|       puDrivesClicked = true;
 | |
|     }
 | |
|     if (ImGui::IsItemHovered())
 | |
|       ImGui::SetTooltip("%s",FileDialog::Instance()->buttonDriveString);
 | |
| #endif // WIN32
 | |
| 
 | |
|     ImGui::SameLine();
 | |
|     
 | |
|     if (IMGUI_BUTTON(editPathButtonString))
 | |
|     {
 | |
|       puInputPathActivated = true;
 | |
|     }
 | |
|     if (ImGui::IsItemHovered())
 | |
|       ImGui::SetTooltip("%s",FileDialog::Instance()->buttonEditPathString);
 | |
| 
 | |
|     ImGui::SameLine();
 | |
| 
 | |
|     ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
 | |
| 
 | |
|     // show current path
 | |
|     if (!prCurrentPathDecomposition.empty())
 | |
|     {
 | |
|       ImGui::SameLine();
 | |
| 
 | |
|       if (puInputPathActivated)
 | |
|       {
 | |
|         ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
 | |
|         ImGui::InputText("##pathedition", puInputPathBuffer, MAX_PATH_BUFFER_SIZE);
 | |
|         ImGui::PopItemWidth();
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         int _id = 0;
 | |
|         for (auto itPathDecomp = prCurrentPathDecomposition.begin();
 | |
|           itPathDecomp != prCurrentPathDecomposition.end(); ++itPathDecomp)
 | |
|         {
 | |
|           if (itPathDecomp != prCurrentPathDecomposition.begin())
 | |
|             ImGui::SameLine();
 | |
|           ImGui::PushID(_id++);
 | |
|           bool click = ImGui::ButtonEx((*itPathDecomp).c_str(),ImVec2(0,0),ImGuiButtonFlags_NoHashTextHide);
 | |
|           ImGui::PopID();
 | |
|           if (click)
 | |
|           {
 | |
|             ComposeNewPath(itPathDecomp);
 | |
|             puPathClicked = true;
 | |
|             break;
 | |
|           }
 | |
|           // activate input for path
 | |
|           if (ImGui::IsItemClicked(ImGuiMouseButton_Right))
 | |
|           {
 | |
|             ComposeNewPath(itPathDecomp);
 | |
|             IGFD::Utils::SetBuffer(puInputPathBuffer, MAX_PATH_BUFFER_SIZE, prCurrentPath);
 | |
|             puInputPathActivated = true;
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileManager::GetResultingPath()
 | |
|   {
 | |
|     std::string path = prCurrentPath;
 | |
| 
 | |
|     if (puDLGDirectoryMode) // if directory mode
 | |
|     {
 | |
|       std::string selectedDirectory = puFileNameBuffer;
 | |
|       if (!selectedDirectory.empty() &&
 | |
|         selectedDirectory != ".")
 | |
|         path += std::string(1u, PATH_SEP) + selectedDirectory;
 | |
|     }
 | |
| 
 | |
|     return path;
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileManager::GetResultingFileName(FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     if (!puDLGDirectoryMode) // if not directory mode
 | |
|     {
 | |
|       return puFileNameBuffer; //vFileDialogInternal.puFilterManager.ReplaceExtentionWithCurrentFilter(std::string(puFileNameBuffer));
 | |
|     }
 | |
| 
 | |
|     return ""; // directory mode
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileManager::GetResultingFilePathName(FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     std::string result = GetResultingPath();
 | |
| 
 | |
|     std::string filename = GetResultingFileName(vFileDialogInternal);
 | |
|     if (!filename.empty())
 | |
|     {
 | |
| #ifdef UNIX
 | |
|       if (puFsRoot != result)
 | |
| #endif // UNIX
 | |
|         result += std::string(1u, PATH_SEP);
 | |
| 
 | |
|       result += filename;
 | |
|     }
 | |
| 
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   std::map<std::string, std::string> IGFD::FileManager::GetResultingSelection()
 | |
|   {
 | |
|     std::map<std::string, std::string> res;
 | |
| 
 | |
|     for (auto& selectedFileName : prSelectedFileNames)
 | |
|     {
 | |
|       std::string result = GetResultingPath();
 | |
| 
 | |
| #ifdef UNIX
 | |
|       if (puFsRoot != result)
 | |
| #endif // UNIX
 | |
|         result += std::string(1u, PATH_SEP);
 | |
| 
 | |
|       result += selectedFileName;
 | |
| 
 | |
|       res[selectedFileName] = result;
 | |
|     }
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// FILE DIALOG INTERNAL ///////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   void IGFD::FileDialogInternal::NewFrame()
 | |
|   {
 | |
|     puCanWeContinue = true;  // reset flag for possibily validate the dialog
 | |
|     puIsOk = false;        // reset dialog result
 | |
|     puFileManager.puDrivesClicked = false;
 | |
|     puFileManager.puPathClicked = false;
 | |
|     
 | |
|     puNeedToExitDialog = false;
 | |
| 
 | |
| #ifdef USE_DIALOG_EXIT_WITH_KEY
 | |
|     if (ImGui::IsKeyPressed(IGFD_EXIT_KEY))
 | |
|     {
 | |
|       // we do that here with the data's defined at the last frame
 | |
|       // because escape key can quit input activation and at the end of the frame all flag will be false
 | |
|       // so we will detect nothing
 | |
|       if (!(puFileManager.puInputPathActivated ||
 | |
|         puSearchManager.puSearchInputIsActive ||
 | |
|         puFileInputIsActive ||
 | |
|         puFileListViewIsActive))
 | |
|       {
 | |
|         puNeedToExitDialog = true; // need to quit dialog
 | |
|       }
 | |
|     }
 | |
|     else
 | |
| #endif
 | |
|     {
 | |
|       puSearchManager.puSearchInputIsActive = false;
 | |
|       puFileInputIsActive = false;
 | |
|       puFileListViewIsActive = false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialogInternal::EndFrame()
 | |
|   {
 | |
|     // directory change
 | |
|     if (puFileManager.puPathClicked)
 | |
|     {
 | |
|       puFileManager.OpenCurrentPath(*this);
 | |
|     }
 | |
| 
 | |
|     if (puFileManager.puDrivesClicked)
 | |
|     {
 | |
|       if (puFileManager.GetDrives())
 | |
|       {
 | |
|         puFileManager.ApplyFilteringOnFileList(*this);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (puFileManager.puInputPathActivated)
 | |
|     {
 | |
|       auto gio = ImGui::GetIO();
 | |
|       if (ImGui::IsKeyReleased(ImGuiKey_Enter))
 | |
|       {
 | |
|         puFileManager.SetCurrentPath(std::string(puFileManager.puInputPathBuffer));
 | |
|         puFileManager.OpenCurrentPath(*this);
 | |
|         puFileManager.puInputPathActivated = false;
 | |
|       }
 | |
|       if (ImGui::IsKeyReleased(ImGuiKey_Escape))
 | |
|       {
 | |
|         puFileManager.puInputPathActivated = false;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialogInternal::ResetForNewDialog()
 | |
|   {
 | |
|     puFileManager.fileListActuallyEmpty=false;
 | |
|   }
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// THUMBNAIL FEATURE //////////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   IGFD::ThumbnailFeature::ThumbnailFeature()
 | |
|   {
 | |
| #ifdef USE_THUMBNAILS
 | |
|     prDisplayMode = DisplayModeEnum::FILE_LIST;
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   IGFD::ThumbnailFeature::~ThumbnailFeature()  = default;
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::NewThumbnailFrame(FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     (void)vFileDialogInternal;
 | |
| 
 | |
| #ifdef USE_THUMBNAILS
 | |
|     prStartThumbnailFileDatasExtraction();
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::EndThumbnailFrame(FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
| #ifdef USE_THUMBNAILS
 | |
|     prClearThumbnails(vFileDialogInternal);
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::QuitThumbnailFrame(FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
| #ifdef USE_THUMBNAILS
 | |
|     prStopThumbnailFileDatasExtraction();
 | |
|     prClearThumbnails(vFileDialogInternal);
 | |
| #endif
 | |
|   }
 | |
| 
 | |
| #ifdef USE_THUMBNAILS
 | |
|   void IGFD::ThumbnailFeature::prStartThumbnailFileDatasExtraction()
 | |
|   {
 | |
|     const bool res = prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable();
 | |
|     if (!res)
 | |
|     {
 | |
|       prIsWorking = true;
 | |
|       prCountFiles = 0U;
 | |
|       prThumbnailGenerationThread = std::shared_ptr<std::thread>(
 | |
|         new std::thread(&IGFD::ThumbnailFeature::prThreadThumbnailFileDatasExtractionFunc, this),
 | |
|         [this](std::thread* obj)
 | |
|         {
 | |
|           prIsWorking = false;
 | |
|           if (obj)
 | |
|             obj->join();
 | |
|         });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool IGFD::ThumbnailFeature::prStopThumbnailFileDatasExtraction()
 | |
|   {
 | |
|     const bool res = prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable();
 | |
|     if (res)
 | |
|     {
 | |
|       prThumbnailGenerationThread.reset();
 | |
|     }
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
|   
 | |
|   void IGFD::ThumbnailFeature::prThreadThumbnailFileDatasExtractionFunc()
 | |
|   {
 | |
|     prCountFiles = 0U;
 | |
|     prIsWorking = true;
 | |
| 
 | |
|     // infinite loop while is thread working
 | |
|     while(prIsWorking)
 | |
|     {
 | |
|       if (!prThumbnailFileDatasToGet.empty())
 | |
|       {
 | |
|         std::shared_ptr<FileInfos> file = nullptr;
 | |
|         prThumbnailFileDatasToGetMutex.lock();
 | |
|         //get the first file in the list
 | |
|         file = (*prThumbnailFileDatasToGet.begin());
 | |
|         prThumbnailFileDatasToGetMutex.unlock();
 | |
| 
 | |
|         // retrieve datas of the texture file if its an image file
 | |
|         if (file.use_count())
 | |
|         {
 | |
|           if (file->fileType == 'f') //-V522
 | |
|           {
 | |
|             if (file->fileExt == ".png"
 | |
|               || file->fileExt == ".bmp"
 | |
|               || file->fileExt == ".tga"
 | |
|               || file->fileExt == ".jpg" || file->fileExt == ".jpeg"
 | |
|               || file->fileExt == ".gif"
 | |
|               || file->fileExt == ".psd"
 | |
|               || file->fileExt == ".pic"
 | |
|               || file->fileExt == ".ppm" || file->fileExt == ".pgm"
 | |
|               //|| file->fileExt == ".hdr" => format float so in few times
 | |
|               )
 | |
|             {
 | |
|               auto fpn = file->filePath + std::string(1u, PATH_SEP) + file->fileNameExt;
 | |
| 
 | |
|               int w = 0;
 | |
|               int h = 0;
 | |
|               int chans = 0;
 | |
|               uint8_t *datas = stbi_load(fpn.c_str(), &w, &h, &chans, STBI_rgb_alpha);
 | |
|               if (datas)
 | |
|               {
 | |
|                 if (w && h)
 | |
|                 {
 | |
|                   // resize with respect to glyph ratio
 | |
|                   const float ratioX = (float)w / (float)h;
 | |
|                   const float newX = DisplayMode_ThumbailsList_ImageHeight * ratioX;
 | |
|                   float newY = w / ratioX;
 | |
|                   if (newX < w) 
 | |
|                     newY = DisplayMode_ThumbailsList_ImageHeight;
 | |
| 
 | |
|                   const auto newWidth = (int)newX;
 | |
|                   const auto newHeight = (int)newY;
 | |
|                   const auto newBufSize = (size_t)(newWidth * newHeight * 4U); //-V112 //-V1028
 | |
|                   auto resizedData = new uint8_t[newBufSize];
 | |
|                   
 | |
|                   const int resizeSucceeded = stbir_resize_uint8(
 | |
|                     datas, w, h, 0,
 | |
|                     resizedData, newWidth, newHeight, 0,
 | |
|                     4); //-V112
 | |
| 
 | |
|                   if (resizeSucceeded)
 | |
|                   {
 | |
|                     auto th = &file->thumbnailInfo;
 | |
| 
 | |
|                     th->textureFileDatas = resizedData;
 | |
|                     th->textureWidth = newWidth;
 | |
|                     th->textureHeight = newHeight;
 | |
|                     th->textureChannels = 4; //-V112
 | |
| 
 | |
|                     // we set that at least, because will launch the gpu creation of the texture in the main thread
 | |
|                     th->isReadyToUpload = true;
 | |
| 
 | |
|                     // need gpu loading
 | |
|                     prAddThumbnailToCreate(file);
 | |
|                   }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                   printf("image loading fail : w:%i h:%i c:%i\n", w, h, 4); //-V112
 | |
|                 }
 | |
| 
 | |
|                 stbi_image_free(datas);
 | |
|               }
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           // peu importe le resultat on vire le fichicer
 | |
|           // remove form this list
 | |
|           // write => thread concurency issues
 | |
|           prThumbnailFileDatasToGetMutex.lock();
 | |
|           prThumbnailFileDatasToGet.pop_front();
 | |
|           prThumbnailFileDatasToGetMutex.unlock();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   inline void inVariadicProgressBar(float fraction, const ImVec2& size_arg, const char* fmt, ...)
 | |
|   {
 | |
|     va_list args;
 | |
|     va_start(args, fmt);
 | |
|     char TempBuffer[512];
 | |
|     const int w = vsnprintf(TempBuffer, 511, fmt, args);
 | |
|     va_end(args);
 | |
|     if (w)
 | |
|     {
 | |
|       ImGui::ProgressBar(fraction, size_arg, TempBuffer);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::prDrawThumbnailGenerationProgress()
 | |
|   {
 | |
|     if (prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable())
 | |
|     {
 | |
|       if (!prThumbnailFileDatasToGet.empty())
 | |
|       {
 | |
|         const auto p = (float)((double)prCountFiles / (double)prThumbnailFileDatasToGet.size()); // read => no thread concurency issues
 | |
|         inVariadicProgressBar(p, ImVec2(50, 0), "%u/%u", prCountFiles, (uint32_t)prThumbnailFileDatasToGet.size()); // read => no thread concurency issues
 | |
|         ImGui::SameLine();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::prAddThumbnailToLoad(const std::shared_ptr<FileInfos>& vFileInfos)
 | |
|   {
 | |
|     if (vFileInfos.use_count())
 | |
|     {
 | |
|       if (vFileInfos->fileType == 'f')
 | |
|       {
 | |
|         if (vFileInfos->fileExt == ".png"
 | |
|           || vFileInfos->fileExt == ".bmp"
 | |
|           || vFileInfos->fileExt == ".tga"
 | |
|           || vFileInfos->fileExt == ".jpg" || vFileInfos->fileExt == ".jpeg"
 | |
|           || vFileInfos->fileExt == ".gif"
 | |
|           || vFileInfos->fileExt == ".psd"
 | |
|           || vFileInfos->fileExt == ".pic"
 | |
|           || vFileInfos->fileExt == ".ppm" || vFileInfos->fileExt == ".pgm"
 | |
|           //|| file->fileExt == ".hdr" => format float so in few times
 | |
|           )
 | |
|         {
 | |
|           // write => thread concurency issues
 | |
|           prThumbnailFileDatasToGetMutex.lock();
 | |
|           prThumbnailFileDatasToGet.push_back(vFileInfos);
 | |
|           vFileInfos->thumbnailInfo.isLoadingOrLoaded = true;
 | |
|           prThumbnailFileDatasToGetMutex.unlock();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   void IGFD::ThumbnailFeature::prAddThumbnailToCreate(const std::shared_ptr<FileInfos>& vFileInfos)
 | |
|   {
 | |
|     if (vFileInfos.use_count())
 | |
|     {
 | |
|       // write => thread concurency issues
 | |
|       prThumbnailToCreateMutex.lock();
 | |
|       prThumbnailToCreate.push_back(vFileInfos);
 | |
|       prThumbnailToCreateMutex.unlock();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::prAddThumbnailToDestroy(const IGFD_Thumbnail_Info& vIGFD_Thumbnail_Info)
 | |
|   {
 | |
|     // write => thread concurency issues
 | |
|     prThumbnailToDestroyMutex.lock();
 | |
|     prThumbnailToDestroy.push_back(vIGFD_Thumbnail_Info);
 | |
|     prThumbnailToDestroyMutex.unlock();
 | |
|   }
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::prDrawDisplayModeToolBar()
 | |
|   {
 | |
|     if (IMGUI_RADIO_BUTTON(DisplayMode_FilesList_ButtonString,
 | |
|       prDisplayMode == DisplayModeEnum::FILE_LIST))
 | |
|       prDisplayMode = DisplayModeEnum::FILE_LIST;
 | |
|     if (ImGui::IsItemHovered())  ImGui::SetTooltip(DisplayMode_FilesList_ButtonHelp);
 | |
|     ImGui::SameLine();
 | |
|     if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsList_ButtonString,
 | |
|       prDisplayMode == DisplayModeEnum::THUMBNAILS_LIST))
 | |
|       prDisplayMode = DisplayModeEnum::THUMBNAILS_LIST;
 | |
|     if (ImGui::IsItemHovered())  ImGui::SetTooltip(DisplayMode_ThumbailsList_ButtonHelp);
 | |
|     ImGui::SameLine();
 | |
|     /* todo
 | |
|     if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsGrid_ButtonString,
 | |
|       prDisplayMode == DisplayModeEnum::THUMBNAILS_GRID))
 | |
|       prDisplayMode = DisplayModeEnum::THUMBNAILS_GRID;
 | |
|     if (ImGui::IsItemHovered())  ImGui::SetTooltip(DisplayMode_ThumbailsGrid_ButtonHelp);
 | |
|     ImGui::SameLine();
 | |
|     */
 | |
|     prDrawThumbnailGenerationProgress();
 | |
|   }
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::prClearThumbnails(FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     // directory wil be changed so the file list will be erased
 | |
|     if (vFileDialogInternal.puFileManager.puPathClicked)
 | |
|     {
 | |
|       size_t count = vFileDialogInternal.puFileManager.GetFullFileListSize();
 | |
|       for (size_t idx = 0U; idx < count; idx++)
 | |
|       {
 | |
|         auto file = vFileDialogInternal.puFileManager.GetFullFileAt(idx);
 | |
|         if (file.use_count())
 | |
|         {
 | |
|           if (file->thumbnailInfo.isReadyToDisplay) //-V522
 | |
|           {
 | |
|             prAddThumbnailToDestroy(file->thumbnailInfo);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::SetCreateThumbnailCallback(const CreateThumbnailFun& vCreateThumbnailFun)
 | |
|   {
 | |
|     prCreateThumbnailFun = vCreateThumbnailFun;
 | |
|   }
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::SetDestroyThumbnailCallback(const DestroyThumbnailFun& vCreateThumbnailFun)
 | |
|   {
 | |
|     prDestroyThumbnailFun = vCreateThumbnailFun;
 | |
|   }
 | |
| 
 | |
|   void IGFD::ThumbnailFeature::ManageGPUThumbnails()
 | |
|   {
 | |
|     if (prCreateThumbnailFun)
 | |
|     {
 | |
|       if (!prThumbnailToCreate.empty())
 | |
|       {
 | |
|         for (const auto& file : prThumbnailToCreate)
 | |
|         {
 | |
|           if (file.use_count())
 | |
|           {
 | |
|             prCreateThumbnailFun(&file->thumbnailInfo);
 | |
|           }
 | |
|         }
 | |
|         prThumbnailToCreateMutex.lock();
 | |
|         prThumbnailToCreate.clear();
 | |
|         prThumbnailToCreateMutex.unlock();
 | |
|       }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       printf("No Callback found for create texture\nYou need to define the callback with a call to SetCreateThumbnailCallback\n");
 | |
|     }
 | |
| 
 | |
|     if (prDestroyThumbnailFun)
 | |
|     {
 | |
|       if (!prThumbnailToDestroy.empty())
 | |
|       {
 | |
|         for (auto thumbnail : prThumbnailToDestroy)
 | |
|         {
 | |
|           prDestroyThumbnailFun(&thumbnail);
 | |
|         }
 | |
|         prThumbnailToDestroyMutex.lock();
 | |
|         prThumbnailToDestroy.clear();
 | |
|         prThumbnailToDestroyMutex.unlock();
 | |
|       }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|     printf("No Callback found for destroy texture\nYou need to define the callback with a call to SetCreateThumbnailCallback\n");
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #endif // USE_THUMBNAILS
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// BOOKMARK FEATURE ///////////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   IGFD::BookMarkFeature::BookMarkFeature()
 | |
|   {
 | |
| #ifdef USE_BOOKMARK
 | |
|     prBookmarkWidth = defaultBookmarkPaneWith;
 | |
| #endif // USE_BOOKMARK
 | |
|   }
 | |
| 
 | |
| #ifdef USE_BOOKMARK
 | |
|   void IGFD::BookMarkFeature::prDrawBookmarkButton()
 | |
|   {
 | |
|     IMGUI_TOGGLE_BUTTON(bookmarksButtonString, &prBookmarkPaneShown);
 | |
| 
 | |
|     if (ImGui::IsItemHovered())
 | |
|       ImGui::SetTooltip(bookmarksButtonHelpString);
 | |
|   }
 | |
|   bool IGFD::BookMarkFeature::prDrawBookmarkPane(FileDialogInternal& vFileDialogInternal, const ImVec2& vSize)
 | |
|   {
 | |
|     bool res = false;
 | |
| 
 | |
|     ImGui::BeginChild("##bookmarkpane", vSize);
 | |
| 
 | |
|     static int selectedBookmarkForEdition = -1;
 | |
| 
 | |
|     if (IMGUI_BUTTON(addBookmarkButtonString "##ImGuiFileDialogAddBookmark"))
 | |
|     {
 | |
|       if (!vFileDialogInternal.puFileManager.IsComposerEmpty())
 | |
|       {
 | |
|         BookmarkStruct bookmark;
 | |
|         bookmark.name = vFileDialogInternal.puFileManager.GetBack();
 | |
|         bookmark.path = vFileDialogInternal.puFileManager.GetCurrentPath();
 | |
|         prBookmarks.push_back(bookmark);
 | |
|       }
 | |
|     }
 | |
|     if (selectedBookmarkForEdition >= 0 &&
 | |
|       selectedBookmarkForEdition < (int)prBookmarks.size())
 | |
|     {
 | |
|       ImGui::SameLine();
 | |
|       if (IMGUI_BUTTON(removeBookmarkButtonString "##ImGuiFileDialogAddBookmark"))
 | |
|       {
 | |
|         prBookmarks.erase(prBookmarks.begin() + selectedBookmarkForEdition);
 | |
|         if (selectedBookmarkForEdition == (int)prBookmarks.size())
 | |
|           selectedBookmarkForEdition--;
 | |
|       }
 | |
| 
 | |
|       if (selectedBookmarkForEdition >= 0 &&
 | |
|         selectedBookmarkForEdition < (int)prBookmarks.size())
 | |
|       {
 | |
|         ImGui::SameLine();
 | |
| 
 | |
|         ImGui::PushItemWidth(vSize.x - ImGui::GetCursorPosX());
 | |
|         if (ImGui::InputText("##ImGuiFileDialogBookmarkEdit", prBookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER))
 | |
|         {
 | |
|           prBookmarks[(size_t)selectedBookmarkForEdition].name = std::string(prBookmarkEditBuffer);
 | |
|         }
 | |
|         ImGui::PopItemWidth();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     ImGui::Separator();
 | |
| 
 | |
|     if (!prBookmarks.empty())
 | |
|     {
 | |
|       prBookmarkClipper.Begin((int)prBookmarks.size(), ImGui::GetTextLineHeightWithSpacing());
 | |
|       while (prBookmarkClipper.Step())
 | |
|       {
 | |
|         for (int i = prBookmarkClipper.DisplayStart; i < prBookmarkClipper.DisplayEnd; i++)
 | |
|         {
 | |
|           if (i < 0) continue;
 | |
|           const BookmarkStruct& bookmark = prBookmarks[(size_t)i];
 | |
|           ImGui::PushID(i);
 | |
|           if (ImGui::Selectable(bookmark.name.c_str(), selectedBookmarkForEdition == i,
 | |
|             ImGuiSelectableFlags_AllowDoubleClick) |
 | |
|             (selectedBookmarkForEdition == -1 &&
 | |
|               bookmark.path == vFileDialogInternal.puFileManager.GetCurrentPath())) // select if path is current
 | |
|           {
 | |
|             selectedBookmarkForEdition = i;
 | |
|             IGFD::Utils::ResetBuffer(prBookmarkEditBuffer);
 | |
|             IGFD::Utils::AppendToBuffer(prBookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER, bookmark.name);
 | |
| 
 | |
|             if (ImGui::IsMouseDoubleClicked(0)) // apply path
 | |
|             {
 | |
|               vFileDialogInternal.puFileManager.SetCurrentPath(bookmark.path);
 | |
|               vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal);
 | |
|               res = true;
 | |
|             }
 | |
|           }
 | |
|           ImGui::PopID();
 | |
|           if (ImGui::IsItemHovered())
 | |
|             ImGui::SetTooltip("%s", bookmark.path.c_str()); //-V111
 | |
|         }
 | |
|       }
 | |
|       prBookmarkClipper.End();
 | |
|     }
 | |
| 
 | |
|     ImGui::EndChild();
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::BookMarkFeature::SerializeBookmarks()
 | |
|   {
 | |
|     std::string res;
 | |
| 
 | |
|     size_t idx = 0;
 | |
|     for (auto& it : prBookmarks)
 | |
|     {
 | |
|       if (idx++ != 0)
 | |
|         res += "##"; // ## because reserved by imgui, so an input text cant have ##
 | |
|       res += it.name + "##" + it.path;
 | |
|     }
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   void IGFD::BookMarkFeature::DeserializeBookmarks(const std::string& vBookmarks)
 | |
|   {
 | |
|     if (!vBookmarks.empty())
 | |
|     {
 | |
|       prBookmarks.clear();
 | |
|       auto arr = IGFD::Utils::SplitStringToVector(vBookmarks, '#', false);
 | |
|       for (size_t i = 0; i < arr.size(); i += 2)
 | |
|       {
 | |
|         BookmarkStruct bookmark;
 | |
|         bookmark.name = arr[i];
 | |
|         if (i + 1 < arr.size()) // for avoid crash if arr size is impair due to user mistake after edition
 | |
|         {
 | |
|           // if bad format we jump this bookmark
 | |
|           bookmark.path = arr[i + 1];
 | |
|           prBookmarks.push_back(bookmark);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| #endif // USE_BOOKMARK
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// KEY EXPLORER FEATURE ///////////////////////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   KeyExplorerFeature::KeyExplorerFeature() = default;
 | |
| 
 | |
| #ifdef USE_EXPLORATION_BY_KEYS
 | |
|   bool IGFD::KeyExplorerFeature::prLocateItem_Loop(FileDialogInternal& vFileDialogInternal, ImWchar vC)
 | |
|   {
 | |
|     bool found = false;
 | |
| 
 | |
|     auto& fdi = vFileDialogInternal.puFileManager;
 | |
|     if (!fdi.IsFilteredListEmpty())
 | |
|     {
 | |
|       auto countFiles = fdi.GetFilteredListSize();
 | |
|       for (size_t i = prLocateFileByInputChar_lastFileIdx; i < countFiles; i++)
 | |
|       {
 | |
|         auto nfo = fdi.GetFilteredFileAt(i);
 | |
|         if (nfo.use_count())
 | |
|         {
 | |
|           if (nfo->fileNameExt_optimized[0] == vC || // lower case search //-V522
 | |
|             nfo->fileNameExt[0] == vC) // maybe upper case search
 | |
|           {
 | |
|             //float p = ((float)i) * ImGui::GetTextLineHeightWithSpacing();
 | |
|             float p = (float)((double)i / (double)countFiles) * ImGui::GetScrollMaxY();
 | |
|             ImGui::SetScrollY(p);
 | |
|             prLocateFileByInputChar_lastFound = true;
 | |
|             prLocateFileByInputChar_lastFileIdx = i;
 | |
|             prStartFlashItem(prLocateFileByInputChar_lastFileIdx);
 | |
| 
 | |
|             auto infos = fdi.GetFilteredFileAt(prLocateFileByInputChar_lastFileIdx);
 | |
|             if (infos.use_count())
 | |
|             {
 | |
|               if (infos->fileType == 'd') //-V522
 | |
|               {
 | |
|                 if (fdi.puDLGDirectoryMode) // directory chooser
 | |
|                 {
 | |
|                   fdi.SelectFileName(vFileDialogInternal, infos);
 | |
|                 }
 | |
|               }
 | |
|               else
 | |
|               {
 | |
|                 fdi.SelectFileName(vFileDialogInternal, infos);
 | |
|               }
 | |
| 
 | |
|               found = true;
 | |
|               break;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return found;
 | |
|   }
 | |
| 
 | |
|   void IGFD::KeyExplorerFeature::prLocateByInputKey(FileDialogInternal& vFileDialogInternal)
 | |
|   {
 | |
|     ImGuiContext& g = *GImGui;
 | |
|     auto& fdi = vFileDialogInternal.puFileManager;
 | |
|     if (!g.ActiveId && !fdi.IsFilteredListEmpty())
 | |
|     {
 | |
|       auto& queueChar = ImGui::GetIO().InputQueueCharacters;
 | |
|       auto countFiles = fdi.GetFilteredListSize();
 | |
| 
 | |
|       // point by char
 | |
|       if (!queueChar.empty())
 | |
|       {
 | |
|         ImWchar c = queueChar.back();
 | |
|         if (prLocateFileByInputChar_InputQueueCharactersSize != queueChar.size())
 | |
|         {
 | |
|           if (c == prLocateFileByInputChar_lastChar) // next file starting with same char until
 | |
|           {
 | |
|             if (prLocateFileByInputChar_lastFileIdx < countFiles - 1U)
 | |
|               prLocateFileByInputChar_lastFileIdx++;
 | |
|             else
 | |
|               prLocateFileByInputChar_lastFileIdx = 0;
 | |
|           }
 | |
| 
 | |
|           if (!prLocateItem_Loop(vFileDialogInternal, c))
 | |
|           {
 | |
|             // not found, loop again from 0 this time
 | |
|             prLocateFileByInputChar_lastFileIdx = 0;
 | |
|             prLocateItem_Loop(vFileDialogInternal, c);
 | |
|           }
 | |
| 
 | |
|           prLocateFileByInputChar_lastChar = c;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       prLocateFileByInputChar_InputQueueCharactersSize = queueChar.size();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void IGFD::KeyExplorerFeature::prExploreWithkeys(FileDialogInternal& vFileDialogInternal, ImGuiID vListViewID)
 | |
|   {
 | |
|     auto& fdi = vFileDialogInternal.puFileManager;
 | |
|     if (!fdi.IsFilteredListEmpty())
 | |
|     {
 | |
|       bool canWeExplore = false;
 | |
|       bool hasNav = (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
 | |
|       
 | |
|       ImGuiContext& g = *GImGui;
 | |
|       if (!hasNav && !g.ActiveId) // no nav and no activated inputs
 | |
|         canWeExplore = true;
 | |
| 
 | |
|       if (g.NavId && g.NavId == vListViewID)
 | |
|       {
 | |
|         if (ImGui::IsKeyPressedMap(IGFD_KEY_ENTER) ||
 | |
|           ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter) ||
 | |
|           ImGui::IsKeyPressedMap(ImGuiKey_Space))
 | |
|         {
 | |
|           ImGui::ActivateItem(vListViewID);
 | |
|           ImGui::SetActiveID(vListViewID, g.CurrentWindow);
 | |
|         }
 | |
|       }
 | |
|       
 | |
|       if (vListViewID == g.LastActiveId-1) // if listview id is the last acticated nav id (ImGui::ActivateItem(vListViewID);)
 | |
|         canWeExplore = true;
 | |
| 
 | |
|       if (canWeExplore)
 | |
|       {
 | |
|         if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
 | |
|         {
 | |
|           ImGui::ClearActiveID();
 | |
|           g.LastActiveId = 0;
 | |
|         }
 | |
| 
 | |
|         auto countFiles = fdi.GetFilteredListSize();
 | |
| 
 | |
|         // explore
 | |
|         bool exploreByKey = false;
 | |
|         bool enterInDirectory = false;
 | |
|         bool exitDirectory = false;
 | |
| 
 | |
|         if ((hasNav && ImGui::IsKeyPressedMap(ImGuiKey_UpArrow)) || (!hasNav && ImGui::IsKeyPressed(IGFD_KEY_UP)))
 | |
|         {
 | |
|           exploreByKey = true;
 | |
|           if (prLocateFileByInputChar_lastFileIdx > 0)
 | |
|             prLocateFileByInputChar_lastFileIdx--;
 | |
|           else
 | |
|             prLocateFileByInputChar_lastFileIdx = countFiles - 1U;
 | |
|         }
 | |
|         else if ((hasNav && ImGui::IsKeyPressedMap(ImGuiKey_DownArrow)) || (!hasNav && ImGui::IsKeyPressed(IGFD_KEY_DOWN)))
 | |
|         {
 | |
|           exploreByKey = true;
 | |
|           if (prLocateFileByInputChar_lastFileIdx < countFiles - 1U)
 | |
|             prLocateFileByInputChar_lastFileIdx++;
 | |
|           else
 | |
|             prLocateFileByInputChar_lastFileIdx = 0U;
 | |
|         }
 | |
|         else if (ImGui::IsKeyReleased(IGFD_KEY_ENTER))
 | |
|         {
 | |
|           exploreByKey = true;
 | |
|           enterInDirectory = true;
 | |
|         }
 | |
|         else if (ImGui::IsKeyReleased(IGFD_KEY_BACKSPACE))
 | |
|         {
 | |
|           exploreByKey = true;
 | |
|           exitDirectory = true;
 | |
|         }
 | |
| 
 | |
|         if (exploreByKey)
 | |
|         {
 | |
|           //float totalHeight = prFilteredFileList.size() * ImGui::GetTextLineHeightWithSpacing();
 | |
|           float p = (float)((double)prLocateFileByInputChar_lastFileIdx / (double)(countFiles - 1U)) * ImGui::GetScrollMaxY();// seems not udpated in tables version outside tables
 | |
|           //float p = ((float)locateFileByInputChar_lastFileIdx) * ImGui::GetTextLineHeightWithSpacing();
 | |
|           ImGui::SetScrollY(p);
 | |
|           prStartFlashItem(prLocateFileByInputChar_lastFileIdx);
 | |
| 
 | |
|           auto infos = fdi.GetFilteredFileAt(prLocateFileByInputChar_lastFileIdx);
 | |
|           if (infos.use_count())
 | |
|           {
 | |
|             if (infos->fileType == 'd') //-V522
 | |
|             {
 | |
|               if (!fdi.puDLGDirectoryMode || enterInDirectory)
 | |
|               {
 | |
|                 if (enterInDirectory)
 | |
|                 {
 | |
|                   if (fdi.SelectDirectory(infos))
 | |
|                   {
 | |
|                     // changement de repertoire
 | |
|                     vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal);
 | |
|                     if (prLocateFileByInputChar_lastFileIdx > countFiles - 1U)
 | |
|                     {
 | |
|                       prLocateFileByInputChar_lastFileIdx = 0;
 | |
|                     }
 | |
|                   }
 | |
|                 }
 | |
|               }
 | |
|               else // directory chooser
 | |
|               {
 | |
|                 fdi.SelectFileName(vFileDialogInternal, infos);
 | |
|               }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|               fdi.SelectFileName(vFileDialogInternal, infos);
 | |
|             }
 | |
| 
 | |
|             if (exitDirectory)
 | |
|             {
 | |
|               auto nfo = std::make_shared<FileInfos>();
 | |
|               nfo->fileNameExt = "..";
 | |
| 
 | |
|               if (fdi.SelectDirectory(nfo))
 | |
|               {
 | |
|                 // changement de repertoire
 | |
|                 vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal);
 | |
|                 if (prLocateFileByInputChar_lastFileIdx > countFiles - 1U)
 | |
|                 {
 | |
|                   prLocateFileByInputChar_lastFileIdx = 0;
 | |
|                 }
 | |
|               }
 | |
| #ifdef WIN32
 | |
|               else
 | |
|               {
 | |
|                 if (fdi.GetComposerSize() == 1U)
 | |
|                 {
 | |
|                   if (fdi.GetDrives())
 | |
|                   {
 | |
|                     fdi.ApplyFilteringOnFileList(vFileDialogInternal);
 | |
|                   }
 | |
|                 }
 | |
|               }
 | |
| #endif // WIN32
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool IGFD::KeyExplorerFeature::prFlashableSelectable(const char* label, bool selected,
 | |
|     ImGuiSelectableFlags flags, bool vFlashing, const ImVec2& size_arg)
 | |
|   {
 | |
|     using namespace ImGui;
 | |
| 
 | |
|     ImGuiWindow* window = GetCurrentWindow();
 | |
|     if (window->SkipItems)
 | |
|       return false;
 | |
| 
 | |
|     ImGuiContext& g = *GImGui;
 | |
|     const ImGuiStyle& style = g.Style;
 | |
| 
 | |
|     // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.
 | |
|     ImGuiID id = window->GetID(label);
 | |
|     ImVec2 label_size = CalcTextSize(label, nullptr, true);
 | |
|     ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); //-V550
 | |
|     ImVec2 pos = window->DC.CursorPos;
 | |
|     pos.y += window->DC.CurrLineTextBaseOffset;
 | |
|     ItemSize(size, 0.0f);
 | |
| 
 | |
|     // Fill horizontal space
 | |
|     // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets.
 | |
|     const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0;
 | |
|     const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x;
 | |
|     const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;
 | |
|     if (fabs(size_arg.x) < FLT_EPSILON || (flags & ImGuiSelectableFlags_SpanAvailWidth))
 | |
|       size.x = ImMax(label_size.x, max_x - min_x);
 | |
| 
 | |
|     // Text stays at the submission position, but bounding box may be extended on both sides
 | |
|     const ImVec2 text_min = pos;
 | |
|     const ImVec2 text_max(min_x + size.x, pos.y + size.y);
 | |
| 
 | |
|     // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.
 | |
|     ImRect bb(min_x, pos.y, text_max.x, text_max.y);
 | |
|     if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0)
 | |
|     {
 | |
|       const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x;
 | |
|       const float spacing_y = style.ItemSpacing.y;
 | |
|       const float spacing_L = IM_FLOOR(spacing_x * 0.50f);
 | |
|       const float spacing_U = IM_FLOOR(spacing_y * 0.50f);
 | |
|       bb.Min.x -= spacing_L;
 | |
|       bb.Min.y -= spacing_U;
 | |
|       bb.Max.x += (spacing_x - spacing_L);
 | |
|       bb.Max.y += (spacing_y - spacing_U);
 | |
|     }
 | |
|     //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }
 | |
| 
 | |
|     // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable..
 | |
|     const float backup_clip_rect_min_x = window->ClipRect.Min.x;
 | |
|     const float backup_clip_rect_max_x = window->ClipRect.Max.x;
 | |
|     if (span_all_columns)
 | |
|     {
 | |
|       window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
 | |
|       window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
 | |
|     }
 | |
| 
 | |
|     bool item_add;
 | |
|     const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;
 | |
|     if (disabled_item)
 | |
|     {
 | |
|       ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
 | |
|       g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
 | |
|       item_add = ItemAdd(bb, id);
 | |
|       g.CurrentItemFlags = backup_item_flags;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       item_add = ItemAdd(bb, id);
 | |
|     }
 | |
| 
 | |
|     if (span_all_columns)
 | |
|     {
 | |
|       window->ClipRect.Min.x = backup_clip_rect_min_x;
 | |
|       window->ClipRect.Max.x = backup_clip_rect_max_x;
 | |
|     }
 | |
| 
 | |
|     if (!item_add)
 | |
|       return false;
 | |
| 
 | |
|     const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
 | |
|     if (disabled_item && !disabled_global) // Only testing this as an optimization
 | |
|       BeginDisabled(true);
 | |
| 
 | |
|     // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,
 | |
|     // which would be advantageous since most selectable are not selected.
 | |
|     if (span_all_columns && window->DC.CurrentColumns)
 | |
|       PushColumnsBackground();
 | |
|     else if (span_all_columns && g.CurrentTable)
 | |
|       TablePushBackgroundChannel();
 | |
| 
 | |
|     // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
 | |
|     ImGuiButtonFlags button_flags = 0;
 | |
|     if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }
 | |
|     if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; }
 | |
|     if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
 | |
|     if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
 | |
|     if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; }
 | |
| 
 | |
|     const bool was_selected = selected;
 | |
|     bool hovered, held;
 | |
|     bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
 | |
| 
 | |
|     // Auto-select when moved into
 | |
|     // - This will be more fully fleshed in the range-select branch
 | |
|     // - This is not exposed as it won't nicely work with some user side handling of shift/control
 | |
|     // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons
 | |
|     //   - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope())
 | |
|     //   - (2) usage will fail with clipped items
 | |
|     //   The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
 | |
|     if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent)
 | |
|       if (g.NavJustMovedToId == id)
 | |
|         selected = pressed = true;
 | |
| 
 | |
|     // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
 | |
|     if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
 | |
|     {
 | |
|       if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
 | |
|       {
 | |
|         SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos));
 | |
|         g.NavDisableHighlight = true;
 | |
|       }
 | |
|     }
 | |
|     if (pressed)
 | |
|       MarkItemEdited(id);
 | |
| 
 | |
|     if (flags & ImGuiSelectableFlags_AllowItemOverlap)
 | |
|       SetItemAllowOverlap();
 | |
| 
 | |
|     // In this branch, Selectable() cannot toggle the selection so this will never trigger.
 | |
|     if (selected != was_selected) //-V547
 | |
|       g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
 | |
| 
 | |
|     // Render
 | |
|     if ((held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) || vFlashing)
 | |
|       hovered = true;
 | |
|     if (hovered || selected)
 | |
|     {
 | |
|       const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
 | |
|       RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
 | |
|     }
 | |
|     RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
 | |
| 
 | |
|     if (span_all_columns && window->DC.CurrentColumns)
 | |
|       PopColumnsBackground();
 | |
|     else if (span_all_columns && g.CurrentTable)
 | |
|       TablePopBackgroundChannel();
 | |
| 
 | |
|     RenderTextClipped(text_min, text_max, label, nullptr, &label_size, style.SelectableTextAlign, &bb);
 | |
| 
 | |
|     // Automatically close popups
 | |
|     if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup))
 | |
|       CloseCurrentPopup();
 | |
| 
 | |
|     if (disabled_item && !disabled_global)
 | |
|       EndDisabled();
 | |
| 
 | |
|     IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
 | |
|     return pressed; //-V1020
 | |
|   }
 | |
| 
 | |
|   void IGFD::KeyExplorerFeature::prStartFlashItem(size_t vIdx)
 | |
|   {
 | |
|     prFlashAlpha = 1.0f;
 | |
|     prFlashedItem = vIdx;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::KeyExplorerFeature::prBeginFlashItem(size_t vIdx)
 | |
|   {
 | |
|     bool res = false;
 | |
| 
 | |
|     if (prFlashedItem == vIdx &&
 | |
|       std::abs(prFlashAlpha - 0.0f) > 0.00001f)
 | |
|     {
 | |
|       prFlashAlpha -= prFlashAlphaAttenInSecs * ImGui::GetIO().DeltaTime;
 | |
|       if (prFlashAlpha < 0.0f) prFlashAlpha = 0.0f;
 | |
| 
 | |
|       ImVec4 hov = ImGui::GetStyleColorVec4(ImGuiCol_HeaderHovered);
 | |
|       hov.w = prFlashAlpha;
 | |
|       ImGui::PushStyleColor(ImGuiCol_HeaderHovered, hov);
 | |
|       res = true;
 | |
|     }
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   void IGFD::KeyExplorerFeature::prEndFlashItem()
 | |
|   {
 | |
|     ImGui::PopStyleColor();
 | |
|   }
 | |
| 
 | |
|   void IGFD::KeyExplorerFeature::SetFlashingAttenuationInSeconds(float vAttenValue)
 | |
|   {
 | |
|     prFlashAlphaAttenInSecs = 1.0f / ImMax(vAttenValue, 0.01f);
 | |
|   }
 | |
| #endif // USE_EXPLORATION_BY_KEYS
 | |
| 
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
|   //// FILE DIALOG CONSTRUCTOR / DESTRUCTOR ///////////////////////////////////////////
 | |
|   /////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {DpiScale=1.0f; singleClickSel=false; mobileMode=false;}
 | |
|   IGFD::FileDialog::~FileDialog() = default;
 | |
| 
 | |
|   //////////////////////////////////////////////////////////////////////////////////////////////////
 | |
|   ///// FILE DIALOG STANDARD DIALOG ////////////////////////////////////////////////////////////////
 | |
|   //////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   // path and fileNameExt can be specified
 | |
|   void IGFD::FileDialog::OpenDialog(
 | |
|     const std::string& vKey,
 | |
|     const std::string& vTitle,
 | |
|     const char* vFilters,
 | |
|     const std::string& vPath,
 | |
|     const std::string& vFileName,
 | |
|     const int& vCountSelectionMax,
 | |
|     UserDatas vUserDatas,
 | |
|     ImGuiFileDialogFlags vFlags,
 | |
|     SelectFun vSelectFun)
 | |
|   {
 | |
|     if (prFileDialogInternal.puShowDialog) // if already opened, quit
 | |
|       return;
 | |
| 
 | |
|     prFileDialogInternal.ResetForNewDialog();
 | |
| 
 | |
|     prFileDialogInternal.puDLGkey = vKey;
 | |
|     prFileDialogInternal.puDLGtitle = vTitle;
 | |
|     prFileDialogInternal.puDLGuserDatas = vUserDatas;
 | |
|     prFileDialogInternal.puDLGflags = vFlags;
 | |
|     prFileDialogInternal.puDLGselFun = vSelectFun;
 | |
|     prFileDialogInternal.puDLGoptionsPane = nullptr;
 | |
|     prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f;
 | |
|     prFileDialogInternal.puDLGmodal = false;
 | |
| 
 | |
|     prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear();
 | |
|     prFileDialogInternal.puFilterManager.ParseFilters(vFilters);
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr);
 | |
|     if (vPath.empty())
 | |
|       prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath();
 | |
|     else
 | |
|       prFileDialogInternal.puFileManager.puDLGpath = vPath;
 | |
|     prFileDialogInternal.puFileManager.SetCurrentPath(vPath);
 | |
|     prFileDialogInternal.puFileManager.puDLGcountSelectionMax = (size_t)vCountSelectionMax;
 | |
|     prFileDialogInternal.puFileManager.SetDefaultFileName(vFileName);
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.ClearAll();
 | |
|     
 | |
|     prFileDialogInternal.puShowDialog = true;          // open dialog
 | |
|   }
 | |
| 
 | |
|   // path and filename are obtained from filePathName
 | |
|   void IGFD::FileDialog::OpenDialog(
 | |
|     const std::string& vKey,
 | |
|     const std::string& vTitle,
 | |
|     const char* vFilters,
 | |
|     const std::string& vFilePathName,
 | |
|     const int& vCountSelectionMax,
 | |
|     UserDatas vUserDatas,
 | |
|     ImGuiFileDialogFlags vFlags,
 | |
|     SelectFun vSelectFun)
 | |
|   {
 | |
|     if (prFileDialogInternal.puShowDialog) // if already opened, quit
 | |
|       return;
 | |
| 
 | |
|     prFileDialogInternal.ResetForNewDialog();
 | |
| 
 | |
|     prFileDialogInternal.puDLGkey = vKey;
 | |
|     prFileDialogInternal.puDLGtitle = vTitle;
 | |
|     prFileDialogInternal.puDLGoptionsPane = nullptr;
 | |
|     prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f;
 | |
|     prFileDialogInternal.puDLGuserDatas = vUserDatas;
 | |
|     prFileDialogInternal.puDLGflags = vFlags;
 | |
|     prFileDialogInternal.puDLGselFun = vSelectFun;
 | |
|     prFileDialogInternal.puDLGmodal = false;
 | |
| 
 | |
|     auto ps = IGFD::Utils::ParsePathFileName(vFilePathName);
 | |
|     if (ps.isOk)
 | |
|     {
 | |
|       prFileDialogInternal.puFileManager.puDLGpath = ps.path;
 | |
|       prFileDialogInternal.puFileManager.SetDefaultFileName("");
 | |
|       prFileDialogInternal.puFilterManager.puDLGdefaultExt = "." + ps.ext;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath();
 | |
|       prFileDialogInternal.puFileManager.SetDefaultFileName("");
 | |
|       prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear();
 | |
|     }
 | |
| 
 | |
|     prFileDialogInternal.puFilterManager.ParseFilters(vFilters);
 | |
|     prFileDialogInternal.puFilterManager.SetSelectedFilterWithExt(
 | |
|       prFileDialogInternal.puFilterManager.puDLGdefaultExt);
 | |
|     
 | |
|     prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath);
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr);
 | |
|     prFileDialogInternal.puFileManager.puDLGcountSelectionMax = vCountSelectionMax; //-V101
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.ClearAll();
 | |
|     
 | |
|     prFileDialogInternal.puShowDialog = true;
 | |
|   }
 | |
| 
 | |
|   // with pane
 | |
|   // path and fileNameExt can be specified
 | |
|   void IGFD::FileDialog::OpenDialog(
 | |
|     const std::string& vKey,
 | |
|     const std::string& vTitle,
 | |
|     const char* vFilters,
 | |
|     const std::string& vPath,
 | |
|     const std::string& vFileName,
 | |
|     const PaneFun& vSidePane,
 | |
|     const float& vSidePaneWidth,
 | |
|     const int& vCountSelectionMax,
 | |
|     UserDatas vUserDatas,
 | |
|     ImGuiFileDialogFlags vFlags,
 | |
|     SelectFun vSelectFun)
 | |
|   {
 | |
|     if (prFileDialogInternal.puShowDialog) // if already opened, quit
 | |
|       return;
 | |
| 
 | |
|     prFileDialogInternal.ResetForNewDialog();
 | |
| 
 | |
|     prFileDialogInternal.puDLGkey = vKey;
 | |
|     prFileDialogInternal.puDLGtitle = vTitle;
 | |
|     prFileDialogInternal.puDLGuserDatas = vUserDatas;
 | |
|     prFileDialogInternal.puDLGflags = vFlags;
 | |
|     prFileDialogInternal.puDLGselFun = vSelectFun;
 | |
|     prFileDialogInternal.puDLGoptionsPane = vSidePane;
 | |
|     prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth;
 | |
|     prFileDialogInternal.puDLGmodal = false;
 | |
| 
 | |
|     prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear();
 | |
|     prFileDialogInternal.puFilterManager.ParseFilters(vFilters);
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.puDLGcountSelectionMax = (size_t)vCountSelectionMax;
 | |
|     prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr);
 | |
|     if (vPath.empty())
 | |
|       prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath();
 | |
|     else
 | |
|       prFileDialogInternal.puFileManager.puDLGpath = vPath;
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath);
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.SetDefaultFileName(vFileName);
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.ClearAll();
 | |
|     
 | |
|     prFileDialogInternal.puShowDialog = true;          // open dialog
 | |
|   }
 | |
| 
 | |
|   // with pane
 | |
|   // path and filename are obtained from filePathName
 | |
|   void IGFD::FileDialog::OpenDialog(
 | |
|     const std::string& vKey,
 | |
|     const std::string& vTitle,
 | |
|     const char* vFilters,
 | |
|     const std::string& vFilePathName,
 | |
|     const PaneFun& vSidePane,
 | |
|     const float& vSidePaneWidth,
 | |
|     const int& vCountSelectionMax,
 | |
|     UserDatas vUserDatas,
 | |
|     ImGuiFileDialogFlags vFlags,
 | |
|     SelectFun vSelectFun)
 | |
|   {
 | |
|     if (prFileDialogInternal.puShowDialog) // if already opened, quit
 | |
|       return;
 | |
| 
 | |
|     prFileDialogInternal.ResetForNewDialog();
 | |
| 
 | |
|     prFileDialogInternal.puDLGkey = vKey;
 | |
|     prFileDialogInternal.puDLGtitle = vTitle;
 | |
|     prFileDialogInternal.puDLGoptionsPane = vSidePane;
 | |
|     prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth;
 | |
|     prFileDialogInternal.puDLGuserDatas = vUserDatas;
 | |
|     prFileDialogInternal.puDLGflags = vFlags;
 | |
|     prFileDialogInternal.puDLGselFun = vSelectFun;
 | |
|     prFileDialogInternal.puDLGmodal = false;
 | |
| 
 | |
|     auto ps = IGFD::Utils::ParsePathFileName(vFilePathName);
 | |
|     if (ps.isOk)
 | |
|     {
 | |
|       prFileDialogInternal.puFileManager.puDLGpath = ps.path;
 | |
|       prFileDialogInternal.puFileManager.SetDefaultFileName(vFilePathName);
 | |
|       prFileDialogInternal.puFilterManager.puDLGdefaultExt = "." + ps.ext;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath();
 | |
|       prFileDialogInternal.puFileManager.SetDefaultFileName("");
 | |
|       prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear();
 | |
|     }
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath);
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.puDLGcountSelectionMax = vCountSelectionMax; //-V101
 | |
|     prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr);
 | |
|     prFileDialogInternal.puFilterManager.ParseFilters(vFilters);
 | |
|     prFileDialogInternal.puFilterManager.SetSelectedFilterWithExt(
 | |
|       prFileDialogInternal.puFilterManager.puDLGdefaultExt);
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.ClearAll();
 | |
| 
 | |
|     prFileDialogInternal.puShowDialog = true;
 | |
|   }
 | |
| 
 | |
|   //////////////////////////////////////////////////////////////////////////////////////////////////
 | |
|   ///// FILE DIALOG MODAL DIALOG ///////////////////////////////////////////////////////////////////
 | |
|   //////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   void IGFD::FileDialog::OpenModal(
 | |
|     const std::string& vKey,
 | |
|     const std::string& vTitle,
 | |
|     const char* vFilters,
 | |
|     const std::string& vPath,
 | |
|     const std::string& vFileName,
 | |
|     const int& vCountSelectionMax,
 | |
|     UserDatas vUserDatas,
 | |
|     ImGuiFileDialogFlags vFlags,
 | |
|     SelectFun vSelectFun)
 | |
|   {
 | |
|     if (prFileDialogInternal.puShowDialog) // if already opened, quit
 | |
|       return;
 | |
| 
 | |
|     OpenDialog(
 | |
|       vKey, vTitle, vFilters,
 | |
|       vPath, vFileName,
 | |
|       vCountSelectionMax, vUserDatas, vFlags, vSelectFun);
 | |
| 
 | |
|     prFileDialogInternal.puDLGmodal = true;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::OpenModal(
 | |
|     const std::string& vKey,
 | |
|     const std::string& vTitle,
 | |
|     const char* vFilters,
 | |
|     const std::string& vFilePathName,
 | |
|     const int& vCountSelectionMax,
 | |
|     UserDatas vUserDatas,
 | |
|     ImGuiFileDialogFlags vFlags,
 | |
|     SelectFun vSelectFun)
 | |
|   {
 | |
|     if (prFileDialogInternal.puShowDialog) // if already opened, quit
 | |
|       return;
 | |
| 
 | |
|     OpenDialog(
 | |
|       vKey, vTitle, vFilters,
 | |
|       vFilePathName,
 | |
|       vCountSelectionMax, vUserDatas, vFlags, vSelectFun);
 | |
| 
 | |
|     prFileDialogInternal.puDLGmodal = true;
 | |
|   }
 | |
| 
 | |
|   // with pane
 | |
|   // path and fileNameExt can be specified
 | |
|   void IGFD::FileDialog::OpenModal(
 | |
|     const std::string& vKey,
 | |
|     const std::string& vTitle,
 | |
|     const char* vFilters,
 | |
|     const std::string& vPath,
 | |
|     const std::string& vFileName,
 | |
|     const PaneFun& vSidePane,
 | |
|     const float& vSidePaneWidth,
 | |
|     const int& vCountSelectionMax,
 | |
|     UserDatas vUserDatas,
 | |
|     ImGuiFileDialogFlags vFlags,
 | |
|     SelectFun vSelectFun)
 | |
|   {
 | |
|     if (prFileDialogInternal.puShowDialog) // if already opened, quit
 | |
|       return;
 | |
| 
 | |
|     OpenDialog(
 | |
|       vKey, vTitle, vFilters,
 | |
|       vPath, vFileName,
 | |
|       vSidePane, vSidePaneWidth,
 | |
|       vCountSelectionMax, vUserDatas, vFlags, vSelectFun);
 | |
| 
 | |
|     prFileDialogInternal.puDLGmodal = true;
 | |
|   }
 | |
| 
 | |
|   // with pane
 | |
|   // path and filename are obtained from filePathName
 | |
|   void IGFD::FileDialog::OpenModal(
 | |
|     const std::string& vKey,
 | |
|     const std::string& vTitle,
 | |
|     const char* vFilters,
 | |
|     const std::string& vFilePathName,
 | |
|     const PaneFun& vSidePane,
 | |
|     const float& vSidePaneWidth,
 | |
|     const int& vCountSelectionMax,
 | |
|     UserDatas vUserDatas,
 | |
|     ImGuiFileDialogFlags vFlags,
 | |
|     SelectFun vSelectFun)
 | |
|   {
 | |
|     if (prFileDialogInternal.puShowDialog) // if already opened, quit
 | |
|       return;
 | |
| 
 | |
|     OpenDialog(
 | |
|       vKey, vTitle, vFilters,
 | |
|       vFilePathName,
 | |
|       vSidePane, vSidePaneWidth,
 | |
|       vCountSelectionMax, vUserDatas, vFlags, vSelectFun);
 | |
| 
 | |
|     prFileDialogInternal.puDLGmodal = true;
 | |
|   }
 | |
| 
 | |
|   //////////////////////////////////////////////////////////////////////////////////////////////////
 | |
|   ///// FILE DIALOG DISPLAY FUNCTION ///////////////////////////////////////////////////////////////
 | |
|   //////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   bool IGFD::FileDialog::Display(const std::string& vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize)
 | |
|   {
 | |
|     bool res = false;
 | |
| 
 | |
|     if (prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey)
 | |
|     {
 | |
|       if (prFileDialogInternal.puUseCustomLocale)
 | |
|         setlocale(prFileDialogInternal.puLocaleCategory, prFileDialogInternal.puLocaleBegin.c_str());
 | |
| 
 | |
|       auto& fdFile = prFileDialogInternal.puFileManager;
 | |
|       auto& fdFilter = prFileDialogInternal.puFilterManager;
 | |
| 
 | |
|       static ImGuiWindowFlags flags;
 | |
| 
 | |
|       // to be sure than only one dialog is displayed per frame
 | |
|       ImGuiContext& g = *GImGui;
 | |
|       if (g.FrameCount == prFileDialogInternal.puLastImGuiFrameCount) // one instance was displayed this frame before for this key +> quit
 | |
|         return res;
 | |
|       prFileDialogInternal.puLastImGuiFrameCount = g.FrameCount; // mark this instance as used this frame
 | |
| 
 | |
|       std::string name = prFileDialogInternal.puDLGtitle + "##" + prFileDialogInternal.puDLGkey;
 | |
|       if (prFileDialogInternal.puName != name)
 | |
|       {
 | |
|         fdFile.ClearComposer();
 | |
|         fdFile.ClearFileLists();
 | |
|         flags = vFlags;
 | |
|       }
 | |
| 
 | |
|       NewFrame();
 | |
| 
 | |
| #ifdef IMGUI_HAS_VIEWPORT
 | |
|       if (!ImGui::GetIO().ConfigViewportsNoDecoration)
 | |
|       {
 | |
|         // https://github.com/ocornut/imgui/issues/4534
 | |
|         ImGuiWindowClass window_class;
 | |
|         window_class.ViewportFlagsOverrideClear = ImGuiViewportFlags_NoDecoration;
 | |
|         ImGui::SetNextWindowClass(&window_class);
 | |
|       }
 | |
| #endif // IMGUI_HAS_VIEWPORT
 | |
| 
 | |
|       ImGui::SetNextWindowSizeConstraints(vMinSize, vMaxSize);
 | |
| 
 | |
|       bool beg = false;
 | |
|       if (prFileDialogInternal.puDLGmodal &&
 | |
|         !prFileDialogInternal.puOkResultToConfirm) // disable modal because the confirm dialog for overwrite is a new modal
 | |
|       {
 | |
|         ImGui::OpenPopup(name.c_str());
 | |
|         beg = ImGui::BeginPopupModal(name.c_str(), (bool*)nullptr,
 | |
|           flags | ImGuiWindowFlags_NoScrollbar);
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         beg = ImGui::Begin(name.c_str(), (bool*)nullptr, flags | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking);
 | |
|       }
 | |
|       if (beg)
 | |
|       {
 | |
|         ImGui::SetWindowPos(ImVec2((ImGui::GetMainViewport()->Size.x-ImGui::GetWindowWidth())*0.5f,(ImGui::GetMainViewport()->Size.y-ImGui::GetWindowHeight())*0.5f));
 | |
|         if (ImGui::GetWindowSize().x<vMinSize.x || ImGui::GetWindowSize().y<vMinSize.y) {
 | |
|           ImGui::SetWindowSize(vMinSize,ImGuiCond_Always);
 | |
|         }
 | |
| #ifdef IMGUI_HAS_VIEWPORT
 | |
|         // if decoration is enabled we disable the resizing feature of imgui for avoid crash with SDL2 and GLFW3
 | |
|         if (ImGui::GetIO().ConfigViewportsNoDecoration)
 | |
|         {
 | |
|           flags = vFlags;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|           auto win = ImGui::GetCurrentWindowRead();
 | |
|           if (win->Viewport->Idx != 0)
 | |
|             flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar;
 | |
|           else
 | |
|             flags = vFlags;
 | |
|         }
 | |
| #endif // IMGUI_HAS_VIEWPORT
 | |
| 
 | |
|         prFileDialogInternal.puName = name; //-V820
 | |
|         puAnyWindowsHovered |= ImGui::IsWindowHovered();
 | |
| 
 | |
|         if (fdFile.puDLGpath.empty())
 | |
|           fdFile.puDLGpath = "."; // defaut path is '.'
 | |
| 
 | |
|         fdFilter.SetDefaultFilterIfNotDefined();
 | |
| 
 | |
|         // init list of files
 | |
|         if (fdFile.IsFileListEmpty() && !fdFile.puShowDrives && !fdFile.fileListActuallyEmpty)
 | |
|         {
 | |
|           IGFD::Utils::ReplaceString(fdFile.puDLGDefaultFileName, fdFile.puDLGpath, ""); // local path
 | |
|           if (!fdFile.puDLGDefaultFileName.empty())
 | |
|           {
 | |
|             fdFile.SetDefaultFileName(fdFile.puDLGDefaultFileName);
 | |
|             fdFilter.SetSelectedFilterWithExt(fdFilter.puDLGdefaultExt);
 | |
|           } else if (fdFile.puDLGDirectoryMode) { // directory mode
 | |
|             fdFile.SetDefaultFileName(".");
 | |
|           }
 | |
|           logV("IGFD: fdFile.IsFileListEmpty() and !fdFile.puShowDrives");
 | |
|           fdFile.ScanDir(prFileDialogInternal, fdFile.puDLGpath);
 | |
|         }
 | |
| 
 | |
|         // draw dialog parts
 | |
|         prDrawHeader(); // bookmark, directory, path
 | |
|         res = prDrawContent(); // bookmark, files view, side pane
 | |
|         bool res1 = prDrawFooter(); // file field, filter combobox, ok/cancel buttons
 | |
|         if (!res) res=res1;
 | |
| 
 | |
|         EndFrame();
 | |
| 
 | |
|         // for display in dialog center, the confirm to overwrite dlg
 | |
|         prFileDialogInternal.puDialogCenterPos = ImGui::GetCurrentWindowRead()->ContentRegionRect.GetCenter();
 | |
| 
 | |
|         // when the confirm to overwrite dialog will appear we need to 
 | |
|         // disable the modal mode of the main file dialog
 | |
|         // see prOkResultToConfirm under
 | |
|         if (prFileDialogInternal.puDLGmodal &&
 | |
|           !prFileDialogInternal.puOkResultToConfirm)
 | |
|           ImGui::EndPopup();
 | |
|       }
 | |
| 
 | |
|       // same things here regarding prOkResultToConfirm
 | |
|       if (!prFileDialogInternal.puDLGmodal || prFileDialogInternal.puOkResultToConfirm)
 | |
|         ImGui::End();
 | |
| 
 | |
|       // confirm the result and show the confirm to overwrite dialog if needed
 | |
|       res =  prConfirm_Or_OpenOverWriteFileDialog_IfNeeded(res, vFlags);
 | |
|       
 | |
|       if (prFileDialogInternal.puUseCustomLocale)
 | |
|         setlocale(prFileDialogInternal.puLocaleCategory, prFileDialogInternal.puLocaleEnd.c_str());
 | |
|     }
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::NewFrame()
 | |
|   {
 | |
|     prFileDialogInternal.NewFrame();
 | |
|     NewThumbnailFrame(prFileDialogInternal);
 | |
|   }
 | |
|   
 | |
|   void IGFD::FileDialog::EndFrame()
 | |
|   {
 | |
|     EndThumbnailFrame(prFileDialogInternal);
 | |
|     prFileDialogInternal.EndFrame();
 | |
|     
 | |
|   }
 | |
|   void IGFD::FileDialog::QuitFrame()
 | |
|   {
 | |
|     QuitThumbnailFrame(prFileDialogInternal);
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::prDrawHeader()
 | |
|   {
 | |
| #ifdef USE_BOOKMARK
 | |
|     prDrawBookmarkButton();
 | |
|     ImGui::SameLine();
 | |
| #endif // USE_BOOKMARK
 | |
| 
 | |
|     prFileDialogInternal.puFileManager.DrawDirectoryCreation(prFileDialogInternal);
 | |
|     ImGui::SameLine();
 | |
|     ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
 | |
|     ImGui::SameLine();
 | |
|     prFileDialogInternal.puFileManager.DrawPathComposer(prFileDialogInternal);
 | |
| 
 | |
| #ifdef USE_THUMBNAILS
 | |
|     if (!(prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableThumbnailMode))
 | |
|     {
 | |
|       prDrawDisplayModeToolBar();
 | |
|       ImGui::SameLine();
 | |
|       ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
 | |
|       ImGui::SameLine();
 | |
|     }
 | |
| #endif // USE_THUMBNAILS
 | |
| 
 | |
|     prFileDialogInternal.puSearchManager.DrawSearchBar(prFileDialogInternal);
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileDialog::prDrawContent()
 | |
|   {
 | |
|     bool escape = false;
 | |
|     ImVec2 size = ImGui::GetContentRegionAvail() - ImVec2(0.0f, prFileDialogInternal.puFooterHeight);
 | |
| 
 | |
| #ifdef USE_BOOKMARK
 | |
|     if (prBookmarkPaneShown)
 | |
|     {
 | |
|       //size.x -= prBookmarkWidth;
 | |
|       float otherWidth = size.x - prBookmarkWidth;
 | |
|       ImGui::PushID("##splitterbookmark");
 | |
|       IGFD::Utils::Splitter(true, 4.0f,
 | |
|         &prBookmarkWidth, &otherWidth, 10.0f,
 | |
|         10.0f + prFileDialogInternal.puDLGoptionsPaneWidth, size.y);
 | |
|       ImGui::PopID();
 | |
|       size.x -= otherWidth;
 | |
|       prDrawBookmarkPane(prFileDialogInternal, size);
 | |
|       ImGui::SameLine();
 | |
|     }
 | |
| #endif // USE_BOOKMARK
 | |
| 
 | |
|     size.x = ImGui::GetContentRegionAvail().x - prFileDialogInternal.puDLGoptionsPaneWidth;
 | |
| 
 | |
|     if (prFileDialogInternal.puDLGoptionsPane)
 | |
|     {
 | |
|       ImGui::PushID("##splittersidepane");
 | |
|       IGFD::Utils::Splitter(true, 4.0f, &size.x, &prFileDialogInternal.puDLGoptionsPaneWidth, 10.0f, 10.0f, size.y);
 | |
|       ImGui::PopID();
 | |
|     }
 | |
| 
 | |
| #ifdef USE_THUMBNAILS
 | |
|     if (prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableThumbnailMode)
 | |
|     {
 | |
|       prDrawFileListView(size);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       switch (prDisplayMode)
 | |
|       {
 | |
|       case DisplayModeEnum::FILE_LIST:
 | |
|         prDrawFileListView(size);
 | |
|         break;
 | |
|       case DisplayModeEnum::THUMBNAILS_LIST:
 | |
|         prDrawThumbnailsListView(size);
 | |
|         break;
 | |
|       case DisplayModeEnum::THUMBNAILS_GRID:
 | |
|         prDrawThumbnailsGridView(size);
 | |
|       }
 | |
|     }
 | |
| #else
 | |
|     escape = prDrawFileListView(size);
 | |
| #endif // USE_THUMBNAILS
 | |
| 
 | |
|     if (prFileDialogInternal.puDLGoptionsPane)
 | |
|     {
 | |
|       prDrawSidePane(size.y);
 | |
|     }
 | |
| 
 | |
|     return escape;
 | |
|   }
 | |
| 
 | |
|   int IGFD::FileDialog::isFileNameValid(const char* n) {
 | |
| #ifdef _WIN32
 | |
|     char lower[6];
 | |
|     int len=strlen(n);
 | |
| 
 | |
|     for (int i=0; i<6; i++) {
 | |
|       lower[i]=0;
 | |
|     }
 | |
| 
 | |
|     if (len>=3) {
 | |
|       if (len>5) len=5;
 | |
|       for (int i=0; i<len; i++) {
 | |
|         if (n[i]>='A' && n[i]<='Z') {
 | |
|           lower[i]=n[i]+('a'-'A');
 | |
|         } else {
 | |
|           lower[i]=n[i];
 | |
|         }
 | |
|       }
 | |
|       if ((memcmp(lower,"con",3)==0 ||
 | |
|            memcmp(lower,"prn",3)==0 ||
 | |
|            memcmp(lower,"aux",3)==0 ||
 | |
|            memcmp(lower,"nul",3)==0) &&
 | |
|           (len==3 || lower[3]=='.')) {
 | |
|         return 2;
 | |
|       }
 | |
|       if (len>=4) {
 | |
|         if ((memcmp(lower,"com",3) ||
 | |
|              memcmp(lower,"lpt",3)) &&
 | |
|             (lower[3]>='1' && lower[3]<='9') &&
 | |
|             (len==4 || lower[4]=='.')) {
 | |
|           return 2;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| #endif
 | |
|     for (const unsigned char* i=(const unsigned char*)n; *i; i++) {
 | |
| #ifdef _WIN32
 | |
|       if (*i<32) {
 | |
|         return 3;
 | |
|       }
 | |
| #endif
 | |
| 
 | |
|       switch (*i) {
 | |
| #ifdef _WIN32
 | |
|         case '<':
 | |
|         case '>':
 | |
|         case ':':
 | |
|         case '"':
 | |
|         case '\\':
 | |
|         case '|':
 | |
|         case '?':
 | |
|         case '*':
 | |
| #endif
 | |
|         case '/':
 | |
|           return 1;
 | |
|       }
 | |
|     }
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileDialog::prDrawFooter()
 | |
|   {
 | |
|     auto& fdFile = prFileDialogInternal.puFileManager;
 | |
|     
 | |
|     float posY = ImGui::GetCursorPos().y; // height of last bar calc
 | |
| 
 | |
|     ImGui::AlignTextToFramePadding();
 | |
|     if (!fdFile.puDLGDirectoryMode)
 | |
|       ImGui::Text("%s",FileDialog::Instance()->fileNameString);
 | |
|     else // directory chooser
 | |
|       ImGui::Text("%s",FileDialog::Instance()->dirNameString);
 | |
| 
 | |
|     ImGui::SameLine();
 | |
| 
 | |
|     // Input file fields
 | |
|     float width = ImGui::GetContentRegionAvail().x;
 | |
|     // fix this! fix this! fix this!
 | |
|     if (!fdFile.puDLGDirectoryMode)
 | |
|       width -= FILTER_COMBO_WIDTH*DpiScale;
 | |
|     ImGui::PushItemWidth(width);
 | |
|     ImGui::InputText("##FileName", fdFile.puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
 | |
|     if (ImGui::GetItemID() == ImGui::GetActiveID())
 | |
|       prFileDialogInternal.puFileInputIsActive = true;
 | |
|     ImGui::PopItemWidth();
 | |
| 
 | |
|     // combobox of filters
 | |
|     prFileDialogInternal.puFilterManager.DrawFilterComboBox(prFileDialogInternal);
 | |
| 
 | |
|     bool res = false;
 | |
| 
 | |
|     // OK Button
 | |
|     bool notEmpty=strlen(fdFile.puFileNameBuffer);
 | |
|     int fileValid=isFileNameValid(fdFile.puFileNameBuffer);
 | |
|     if (!(prFileDialogInternal.puDLGflags&ImGuiFileDialogFlags_ConfirmOverwrite)) fileValid=0;
 | |
|     ImGui::BeginDisabled(!(prFileDialogInternal.puCanWeContinue && notEmpty && fileValid==0));
 | |
|     std::string okbs=std::string(FileDialog::Instance()->okButtonString)+"##validationdialog";
 | |
|     if (IMGUI_BUTTON(okbs.c_str()))
 | |
|     {
 | |
|       prFileDialogInternal.puIsOk = true;
 | |
|       res = true;
 | |
|     }
 | |
|     ImGui::EndDisabled();
 | |
|     if (!(prFileDialogInternal.puCanWeContinue && notEmpty && fileValid==0)) {
 | |
|       if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
 | |
|         if (!notEmpty) {
 | |
|           if (prFileDialogInternal.puDLGflags&ImGuiFileDialogFlags_ConfirmOverwrite) {
 | |
|             ImGui::SetTooltip("file name is empty");
 | |
|           } else {
 | |
|             ImGui::SetTooltip("select a file");
 | |
|           }
 | |
|         } else if (!prFileDialogInternal.puCanWeContinue) {
 | |
|           ImGui::SetTooltip("we can't continue - this is most likely a bug!");
 | |
|         } else switch (fileValid) {
 | |
|           case 1:
 | |
| #ifdef _WIN32
 | |
|             ImGui::SetTooltip("invalid characters in file name\nmake sure there aren't any of these:\n  < > : \" / \\ | ? *");
 | |
| #else
 | |
|             ImGui::SetTooltip("invalid characters in file name\nmake sure there aren't any slashes (/)");
 | |
| #endif
 | |
|             break;
 | |
|           case 2:
 | |
|             ImGui::SetTooltip("this file name is reserved by the system");
 | |
|             break;
 | |
|           case 3:
 | |
|             ImGui::SetTooltip("non-printable characters in file name\nhow did you manage to type these?");
 | |
|             break;
 | |
|           default:
 | |
|             ImGui::SetTooltip("bug! how did you do this? please fill an issue report now...");
 | |
|             break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     ImGui::SameLine();
 | |
| 
 | |
|     // Cancel Button
 | |
|     std::string cbs=std::string(cancelButtonString)+"##validationdialog";
 | |
|     if (IMGUI_BUTTON(cbs.c_str()) || 
 | |
|       prFileDialogInternal.puNeedToExitDialog) // dialog exit asked
 | |
|     {
 | |
|       prFileDialogInternal.puIsOk = false;
 | |
|       res = true;
 | |
|     }
 | |
| 
 | |
|     prFileDialogInternal.puFooterHeight = ImGui::GetCursorPosY() - posY;
 | |
| 
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   // returns 0 if not break loop, 1 if break loop, 2 if exit dialog
 | |
|   int IGFD::FileDialog::prSelectableItem(int vidx, const FileInfos& vInfos, bool vSelected, const char* vFmt, ...)
 | |
|   {
 | |
|     auto& fdi = prFileDialogInternal.puFileManager;
 | |
| 
 | |
|     static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick |
 | |
|       ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth | ImGuiSelectableFlags_NoHashTextHide;
 | |
| 
 | |
|                 // TODO BUG?!
 | |
|                 // YES BUG: THIS JUST CRASHED FOR SOME REASON
 | |
|     va_list args;
 | |
|     va_start(args, vFmt);
 | |
|     vsnprintf(fdi.puVariadicBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFmt, args);
 | |
|     va_end(args);
 | |
| 
 | |
|     float h = /*mobileMode?(ImGui::GetFontSize()+10.0f*DpiScale):*/0.0f;
 | |
| #ifdef USE_THUMBNAILS
 | |
|     if (prDisplayMode == DisplayModeEnum::THUMBNAILS_LIST)
 | |
|       h = DisplayMode_ThumbailsList_ImageHeight;
 | |
| #endif // USE_THUMBNAILS
 | |
| #ifdef USE_EXPLORATION_BY_KEYS
 | |
|     bool flashed = prBeginFlashItem((size_t)vidx);
 | |
|     bool res = prFlashableSelectable(fdi.puVariadicBuffer, vSelected, selectableFlags,
 | |
|       flashed, ImVec2(-1.0f, h));
 | |
|     if (flashed)
 | |
|       prEndFlashItem();
 | |
| #else // USE_EXPLORATION_BY_KEYS
 | |
|     (void)vidx; // remove a warnings ofr unused var
 | |
| 
 | |
|     bool res = ImGui::Selectable(fdi.puVariadicBuffer, vSelected, selectableFlags, ImVec2(-1.0f, h));
 | |
| #endif // USE_EXPLORATION_BY_KEYS
 | |
|     if (res)
 | |
|     {
 | |
|       if (vInfos.fileType == 'd')
 | |
|       {
 | |
|         bool isSelectingDir=false;
 | |
|         // nav system, selectebale cause open directory or select directory
 | |
|         if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
 | |
|         {
 | |
|           if (fdi.puDLGDirectoryMode) // directory chooser
 | |
|           {
 | |
|             fdi.SelectFileName(prFileDialogInternal, vInfos);
 | |
|           }
 | |
|           else
 | |
|           {
 | |
|             fdi.puPathClicked = fdi.SelectDirectory(vInfos);
 | |
|             isSelectingDir=true;
 | |
|           }
 | |
|         }
 | |
|         else // no nav system => classic behavior
 | |
|         {
 | |
|           if (DOUBLE_CLICKED) // 0 -> left mouse button double click
 | |
|           {
 | |
|             isSelectingDir=true;
 | |
|             fdi.puPathClicked = fdi.SelectDirectory(vInfos);
 | |
|           }
 | |
|           else if (fdi.puDLGDirectoryMode) // directory chooser
 | |
|           {
 | |
|             fdi.SelectFileName(prFileDialogInternal, vInfos);
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         return isSelectingDir; // needToBreakTheloop
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         if (DOUBLE_CLICKED) {
 | |
|           fdi.SelectFileName(prFileDialogInternal, vInfos);
 | |
|           prFileDialogInternal.puIsOk = true;
 | |
|           return 2;
 | |
|         } else {
 | |
|           fdi.SelectFileName(prFileDialogInternal, vInfos);
 | |
|           if (prFileDialogInternal.puDLGselFun!=NULL) {
 | |
|             std::string argPath;
 | |
|             for (auto& i: GetSelection()) {
 | |
|               argPath=i.second;
 | |
|             }
 | |
|             if (!argPath.empty()) {
 | |
|               prFileDialogInternal.puDLGselFun(argPath.c_str());
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::prBeginFileColorIconStyle(const FileInfos& vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont)
 | |
|   {
 | |
|     vOutStr.clear();
 | |
|     vOutShowColor = false;
 | |
| 
 | |
|     if (vFileInfos.fileStyle.use_count()) //-V807 //-V522
 | |
|     {
 | |
|       vOutShowColor = true;
 | |
| 
 | |
|       *vOutFont = vFileInfos.fileStyle->font;
 | |
|     }
 | |
| 
 | |
|     if (vOutShowColor && !vFileInfos.fileStyle->icon.empty()) vOutStr = vFileInfos.fileStyle->icon;
 | |
|     else if (vFileInfos.fileType == 'd') vOutStr = dirEntryString;
 | |
|     else if (vFileInfos.fileType == 'l') vOutStr = linkEntryString;
 | |
|     else if (vFileInfos.fileType == 'f') vOutStr = fileEntryString;
 | |
| 
 | |
|     vOutStr += " " + vFileInfos.fileNameExt;
 | |
| 
 | |
|     if (vOutShowColor)
 | |
|       ImGui::PushStyleColor(ImGuiCol_Text, vFileInfos.fileStyle->color);
 | |
|     if (*vOutFont)
 | |
|       ImGui::PushFont(*vOutFont);
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::prEndFileColorIconStyle(const bool& vShowColor, ImFont* vFont)
 | |
|   {
 | |
|     if (vFont)
 | |
|       ImGui::PopFont();
 | |
|     if (vShowColor)
 | |
|       ImGui::PopStyleColor();
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileDialog::prDrawFileListView(ImVec2 vSize)
 | |
|   {
 | |
|     bool escape = false;
 | |
|     auto& fdi = prFileDialogInternal.puFileManager;
 | |
| 
 | |
|     ImGui::PushID(this);
 | |
| 
 | |
|     static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg |
 | |
|       ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY |
 | |
|       ImGuiTableFlags_NoHostExtendY
 | |
| #ifndef USE_CUSTOM_SORTING_ICON
 | |
|       | ImGuiTableFlags_Sortable
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|       ;
 | |
|     auto listViewID = ImGui::GetID("##FileDialog_fileTable");
 | |
|     if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 4, flags, vSize, 0.0f)) //-V112
 | |
|     {
 | |
|       ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible
 | |
|       ImGui::TableSetupColumn(fdi.puHeaderFileName.c_str(), ImGuiTableColumnFlags_WidthStretch, -1, 0);
 | |
|       ImGui::TableSetupColumn(fdi.puHeaderFileType.c_str(), ImGuiTableColumnFlags_WidthFixed | 
 | |
|         ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 1);
 | |
|       ImGui::TableSetupColumn(fdi.puHeaderFileSize.c_str(), ImGuiTableColumnFlags_WidthFixed | 
 | |
|         ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 2);
 | |
|       ImGui::TableSetupColumn(fdi.puHeaderFileDate.c_str(), ImGuiTableColumnFlags_WidthFixed | 
 | |
|         ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 3);
 | |
| 
 | |
| #ifndef USE_CUSTOM_SORTING_ICON
 | |
|       // Sort our data if sort specs have been changed!
 | |
|       if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs())
 | |
|       {
 | |
|         if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty())
 | |
|         {
 | |
|           if (sorts_specs->Specs->ColumnUserID == 0)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true);
 | |
|           else if (sorts_specs->Specs->ColumnUserID == 1)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true);
 | |
|           else if (sorts_specs->Specs->ColumnUserID == 2)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true);
 | |
|           else //if (sorts_specs->Specs->ColumnUserID == 3) => alwayd true for the moment, to uncomment if we add a fourth column
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true);
 | |
| 
 | |
|           sorts_specs->SpecsDirty = false;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       ImGui::TableHeadersRow();
 | |
| #else // USE_CUSTOM_SORTING_ICON
 | |
|       ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
 | |
|       for (int column = 0; column < 4; column++) //-V112
 | |
|       {
 | |
|         ImGui::TableSetColumnIndex(column);
 | |
|         const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()
 | |
|         ImGui::PushID(column);
 | |
|         ImGui::TableHeader(column_name);
 | |
|         ImGui::PopID();
 | |
|         if (ImGui::IsItemClicked())
 | |
|         {
 | |
|           if (column == 0)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true);
 | |
|           else if (column == 1)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true);
 | |
|           else if (column == 2)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true);
 | |
|           else //if (column == 3) => alwayd true for the moment, to uncomment if we add a fourth column
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true);
 | |
|         }
 | |
|       }
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|       if (!fdi.IsFilteredListEmpty())
 | |
|       {
 | |
|         std::string _str;
 | |
|         ImFont* _font = nullptr;
 | |
|         bool _showColor = false;
 | |
|         
 | |
|         prFileListClipper.Begin((int)fdi.GetFilteredListSize(), ImGui::GetTextLineHeightWithSpacing());
 | |
|         while (prFileListClipper.Step())
 | |
|         {
 | |
|           for (int i = prFileListClipper.DisplayStart; i < prFileListClipper.DisplayEnd; i++)
 | |
|           {
 | |
|             if (i < 0) continue;
 | |
| 
 | |
|             const FileInfos& infos = fdi.GetFilteredFileAt((size_t)i);
 | |
|             if (!infos.isValid)
 | |
|               continue;
 | |
| 
 | |
|             prBeginFileColorIconStyle(infos, _showColor, _str, &_font);
 | |
|           
 | |
|             bool selected = fdi.IsFileNameSelected(infos.fileNameExt); // found
 | |
| 
 | |
|             ImGui::TableNextRow();
 | |
| 
 | |
|             int needToBreakTheloop = false;
 | |
| 
 | |
|             if (ImGui::TableNextColumn()) // file name
 | |
|             {
 | |
|              // TODO BUG?!?!?!
 | |
|              // YES BUG
 | |
|               needToBreakTheloop = prSelectableItem(i, infos, selected, "%s", _str.c_str());
 | |
|               if (needToBreakTheloop==2) escape=true;
 | |
|             }
 | |
|             if (ImGui::TableNextColumn()) // file type
 | |
|             {
 | |
|               ImGui::Text("%s", infos.fileExt.c_str());
 | |
|             }
 | |
|             if (ImGui::TableNextColumn()) // file size
 | |
|             {
 | |
|               if (infos.fileType != 'd')
 | |
|               {
 | |
|                 ImGui::Text("%s ", infos.formatedFileSize.c_str());
 | |
|               }
 | |
|               else
 | |
|               {
 | |
|                 ImGui::Text("%s","");
 | |
|               }
 | |
|             }
 | |
|             if (ImGui::TableNextColumn()) // file date + time
 | |
|             {
 | |
|               ImGui::Text("%s", infos.fileModifDate.c_str());
 | |
|             }
 | |
| 
 | |
|             prEndFileColorIconStyle(_showColor, _font);
 | |
| 
 | |
|             if (needToBreakTheloop==1)
 | |
|               break;
 | |
|           }
 | |
|         }
 | |
|         prFileListClipper.End();
 | |
|       }
 | |
| 
 | |
| #ifdef USE_EXPLORATION_BY_KEYS
 | |
|       if (!fdi.puInputPathActivated)
 | |
|       {
 | |
|         prLocateByInputKey(prFileDialogInternal);
 | |
|         prExploreWithkeys(prFileDialogInternal, listViewID);
 | |
|       }
 | |
| #endif // USE_EXPLORATION_BY_KEYS
 | |
| 
 | |
|       ImGuiContext& g = *GImGui;
 | |
|       if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID)
 | |
|       {
 | |
|         prFileDialogInternal.puFileListViewIsActive = true;
 | |
|       }
 | |
| 
 | |
|       ImGui::EndTable();
 | |
|     }
 | |
| 
 | |
|     ImGui::PopID();
 | |
|     return escape;
 | |
|   }
 | |
| 
 | |
| #ifdef USE_THUMBNAILS
 | |
|   void IGFD::FileDialog::prDrawThumbnailsListView(ImVec2 vSize)
 | |
|   {
 | |
|     auto& fdi = prFileDialogInternal.puFileManager;
 | |
| 
 | |
|     ImGui::PushID(this);
 | |
| 
 | |
|     static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg |
 | |
|       ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY |
 | |
|       ImGuiTableFlags_NoHostExtendY
 | |
| #ifndef USE_CUSTOM_SORTING_ICON
 | |
|       | ImGuiTableFlags_Sortable
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|       ;
 | |
|     auto listViewID = ImGui::GetID("##FileDialog_fileTable");
 | |
|     if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 5, flags, vSize, 0.0f))
 | |
|     {
 | |
|       ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible
 | |
|       ImGui::TableSetupColumn(fdi.puHeaderFileName.c_str(), ImGuiTableColumnFlags_WidthStretch, -1, 0);
 | |
|       ImGui::TableSetupColumn(fdi.puHeaderFileType.c_str(), ImGuiTableColumnFlags_WidthFixed |
 | |
|         ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 1);
 | |
|       ImGui::TableSetupColumn(fdi.puHeaderFileSize.c_str(), ImGuiTableColumnFlags_WidthFixed |
 | |
|         ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 2);
 | |
|       ImGui::TableSetupColumn(fdi.puHeaderFileDate.c_str(), ImGuiTableColumnFlags_WidthFixed |
 | |
|         ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 3);
 | |
|       // not needed to have an option for hide the thumbnails since this is why this view is used
 | |
|       ImGui::TableSetupColumn(fdi.puHeaderFileThumbnails.c_str(), ImGuiTableColumnFlags_WidthFixed, -1, 4); //-V112
 | |
| 
 | |
| #ifndef USE_CUSTOM_SORTING_ICON
 | |
|       // Sort our data if sort specs have been changed!
 | |
|       if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs())
 | |
|       {
 | |
|         if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty())
 | |
|         {
 | |
|           if (sorts_specs->Specs->ColumnUserID == 0)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true);
 | |
|           else if (sorts_specs->Specs->ColumnUserID == 1)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true);
 | |
|           else if (sorts_specs->Specs->ColumnUserID == 2)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true);
 | |
|           else if (sorts_specs->Specs->ColumnUserID == 3)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true);
 | |
|           else // if (sorts_specs->Specs->ColumnUserID == 4) = > always true for the moment, to uncomment if we add another column
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS, true);
 | |
|           sorts_specs->SpecsDirty = false;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       ImGui::TableHeadersRow();
 | |
| #else // USE_CUSTOM_SORTING_ICON
 | |
|       ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
 | |
|       for (int column = 0; column < 5; column++)
 | |
|       {
 | |
|         ImGui::TableSetColumnIndex(column);
 | |
|         const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()
 | |
|         ImGui::PushID(column);
 | |
|         ImGui::TableHeader(column_name);
 | |
|         ImGui::PopID();
 | |
|         if (ImGui::IsItemClicked())
 | |
|         {
 | |
|           if (column == 0)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true);
 | |
|           else if (column == 1)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true);
 | |
|           else if (column == 2)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true);
 | |
|           else if (column == 3)
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true);
 | |
|           else // if (column == 4) = > always true for the moment, to uncomment if we add another column
 | |
|             fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS, true);
 | |
|         }
 | |
|       }
 | |
| #endif // USE_CUSTOM_SORTING_ICON
 | |
|       if (!fdi.IsFilteredListEmpty())
 | |
|       {
 | |
|         std::string _str;
 | |
|         ImFont* _font = nullptr;
 | |
|         bool _showColor = false;
 | |
| 
 | |
|         ImGuiContext& g = *GImGui;
 | |
|         const float itemHeight = ImMax(g.FontSize, DisplayMode_ThumbailsList_ImageHeight) + g.Style.ItemSpacing.y;
 | |
| 
 | |
|         prFileListClipper.Begin((int)fdi.GetFilteredListSize(), itemHeight);
 | |
|         while (prFileListClipper.Step())
 | |
|         {
 | |
|           for (int i = prFileListClipper.DisplayStart; i < prFileListClipper.DisplayEnd; i++)
 | |
|           {
 | |
|             if (i < 0) continue;
 | |
| 
 | |
|             auto infos = fdi.GetFilteredFileAt((size_t)i);
 | |
|             if (!infos.use_count())
 | |
|               continue;
 | |
| 
 | |
|             prBeginFileColorIconStyle(infos, _showColor, _str, &_font);
 | |
| 
 | |
|             bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found
 | |
| 
 | |
|             ImGui::TableNextRow();
 | |
| 
 | |
|             bool needToBreakTheloop = false;
 | |
| 
 | |
|             if (ImGui::TableNextColumn()) // file name
 | |
|             {
 | |
|               needToBreakTheloop = prSelectableItem(i, infos, selected, _str.c_str());
 | |
|             }
 | |
|             if (ImGui::TableNextColumn()) // file type
 | |
|             {
 | |
|               ImGui::Text("%s", infos->fileExt.c_str());
 | |
|             }
 | |
|             if (ImGui::TableNextColumn()) // file size
 | |
|             {
 | |
|               if (infos->fileType != 'd')
 | |
|               {
 | |
|                 ImGui::Text("%s ", infos->formatedFileSize.c_str());
 | |
|               }
 | |
|               else
 | |
|               {
 | |
|                 ImGui::Text("");
 | |
|               }
 | |
|             }
 | |
|             if (ImGui::TableNextColumn()) // file date + time
 | |
|             {
 | |
|               ImGui::Text("%s", infos->fileModifDate.c_str());
 | |
|             }
 | |
|             if (ImGui::TableNextColumn()) // file thumbnails
 | |
|             {
 | |
|               auto th = &infos->thumbnailInfo;
 | |
| 
 | |
|               if (!th->isLoadingOrLoaded)
 | |
|               {
 | |
|                 prAddThumbnailToLoad(infos);
 | |
|               }
 | |
|               if (th->isReadyToDisplay &&
 | |
|                 th->textureID)
 | |
|               {
 | |
|                 ImGui::Image((ImTextureID)th->textureID,
 | |
|                   ImVec2((float)th->textureWidth,
 | |
|                     (float)th->textureHeight));
 | |
|               }
 | |
|             }
 | |
| 
 | |
|             prEndFileColorIconStyle(_showColor, _font);
 | |
| 
 | |
|             if (needToBreakTheloop)
 | |
|               break;
 | |
|           }
 | |
|         }
 | |
|         prFileListClipper.End();
 | |
|       }
 | |
| 
 | |
| #ifdef USE_EXPLORATION_BY_KEYS
 | |
|       if (!fdi.puInputPathActivated)
 | |
|       {
 | |
|         prLocateByInputKey(prFileDialogInternal);
 | |
|         prExploreWithkeys(prFileDialogInternal, listViewID);
 | |
|       }
 | |
| #endif // USE_EXPLORATION_BY_KEYS
 | |
| 
 | |
|       ImGuiContext& g = *GImGui;
 | |
|       if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID)
 | |
|       {
 | |
|         prFileDialogInternal.puFileListViewIsActive = true;
 | |
|       }
 | |
| 
 | |
|       ImGui::EndTable();
 | |
|     }
 | |
| 
 | |
|     ImGui::PopID();
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::prDrawThumbnailsGridView(ImVec2 vSize)
 | |
|   {
 | |
|     if (ImGui::BeginChild("##thumbnailsGridsFiles", vSize))
 | |
|     {
 | |
|       // todo
 | |
|     }
 | |
| 
 | |
|     ImGui::EndChild();
 | |
|   }
 | |
| 
 | |
| #endif
 | |
| 
 | |
|   void IGFD::FileDialog::prDrawSidePane(float vHeight)
 | |
|   {
 | |
|     ImGui::SameLine();
 | |
| 
 | |
|     ImGui::BeginChild("##FileTypes", ImVec2(0, vHeight));
 | |
| 
 | |
|     prFileDialogInternal.puDLGoptionsPane(
 | |
|       prFileDialogInternal.puFilterManager.GetSelectedFilter().filter.c_str(), 
 | |
|       prFileDialogInternal.puDLGuserDatas, &prFileDialogInternal.puCanWeContinue);
 | |
| 
 | |
|     ImGui::EndChild();
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::Close()
 | |
|   {
 | |
|     prFileDialogInternal.puDLGkey.clear();
 | |
|     prFileDialogInternal.puShowDialog = false;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileDialog::WasOpenedThisFrame(const std::string& vKey) const
 | |
|   {
 | |
|     bool res = prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey;
 | |
|     if (res)
 | |
|     {
 | |
|       ImGuiContext& g = *GImGui;
 | |
|       res &= prFileDialogInternal.puLastImGuiFrameCount == g.FrameCount; // return true if a dialog was displayed in this frame
 | |
|     }
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileDialog::WasOpenedThisFrame() const
 | |
|   {
 | |
|     bool res = prFileDialogInternal.puShowDialog;
 | |
|     if (res)
 | |
|     {
 | |
|       ImGuiContext& g = *GImGui;
 | |
|       res &= prFileDialogInternal.puLastImGuiFrameCount == g.FrameCount; // return true if a dialog was displayed in this frame
 | |
|     }
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileDialog::IsOpened(const std::string& vKey) const
 | |
|   {
 | |
|     return (prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey);
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileDialog::IsOpened() const
 | |
|   {
 | |
|     return prFileDialogInternal.puShowDialog;
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileDialog::GetOpenedKey() const
 | |
|   {
 | |
|     if (prFileDialogInternal.puShowDialog)
 | |
|       return prFileDialogInternal.puDLGkey;
 | |
|     return "";
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileDialog::GetFilePathName()
 | |
|   {
 | |
|     return prFileDialogInternal.puFileManager.GetResultingFilePathName(prFileDialogInternal);
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileDialog::GetCurrentPath()
 | |
|   {
 | |
|     return prFileDialogInternal.puFileManager.GetResultingPath();
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileDialog::GetCurrentFileName()
 | |
|   {
 | |
|     return prFileDialogInternal.puFileManager.GetResultingFileName(prFileDialogInternal);
 | |
|   }
 | |
| 
 | |
|   std::string IGFD::FileDialog::GetCurrentFilter()
 | |
|   {
 | |
|     return prFileDialogInternal.puFilterManager.GetSelectedFilter().filter;
 | |
|   }
 | |
| 
 | |
|   std::map<std::string, std::string> IGFD::FileDialog::GetSelection()
 | |
|   {
 | |
|     return prFileDialogInternal.puFileManager.GetResultingSelection();
 | |
|   }
 | |
| 
 | |
|   UserDatas IGFD::FileDialog::GetUserDatas() const
 | |
|   {
 | |
|     return prFileDialogInternal.puDLGuserDatas;
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileDialog::IsOk() const
 | |
|   {
 | |
|     return prFileDialogInternal.puIsOk;
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos)
 | |
|   {
 | |
|     prFileDialogInternal.puFilterManager.SetFileStyle(vFlags, vCriteria, vInfos);
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont)
 | |
|   {
 | |
|     prFileDialogInternal.puFilterManager.SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont);
 | |
|   }
 | |
| 
 | |
|   bool IGFD::FileDialog::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont **vOutFont)
 | |
|   {
 | |
|     return prFileDialogInternal.puFilterManager.GetFileStyle(vFlags, vCriteria, vOutColor, vOutIcon, vOutFont);
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::ClearFilesStyle()
 | |
|   {
 | |
|     prFileDialogInternal.puFilterManager.ClearFilesStyle();
 | |
|   }
 | |
| 
 | |
|   void IGFD::FileDialog::SetLocales(const int& vLocaleCategory, const std::string& vLocaleBegin, const std::string& vLocaleEnd)
 | |
|   {
 | |
|     prFileDialogInternal.puUseCustomLocale = true;
 | |
|     prFileDialogInternal.puLocaleBegin = vLocaleBegin;
 | |
|     prFileDialogInternal.puLocaleEnd = vLocaleEnd;
 | |
|   }
 | |
| 
 | |
|   //////////////////////////////////////////////////////////////////////////////
 | |
|   //// OVERWRITE DIALOG ////////////////////////////////////////////////////////
 | |
|   //////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
|   bool IGFD::FileDialog::prConfirm_Or_OpenOverWriteFileDialog_IfNeeded(bool vLastAction, ImGuiWindowFlags vFlags)
 | |
|   {
 | |
|     // if confirmation => return true for confirm the overwrite et quit the dialog
 | |
|     // if cancel => return false && set IsOk to false for keep inside the dialog
 | |
| 
 | |
|     // if IsOk == false => return false for quit the dialog
 | |
|     if (!prFileDialogInternal.puIsOk && vLastAction)
 | |
|     {
 | |
|       QuitFrame();
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     // if IsOk == true && no check of overwrite => return true for confirm the dialog
 | |
|     if (prFileDialogInternal.puIsOk && vLastAction && !(prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_ConfirmOverwrite))
 | |
|     {
 | |
|       QuitFrame();
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     // if IsOk == true && check of overwrite => return false and show confirm to overwrite dialog
 | |
|     if ((prFileDialogInternal.puOkResultToConfirm || (prFileDialogInternal.puIsOk && vLastAction)) && 
 | |
|       (prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_ConfirmOverwrite))
 | |
|     {
 | |
|       if (prFileDialogInternal.puIsOk) // catched only one time
 | |
|       {
 | |
|         if (!prFileDialogInternal.puFileManager.IsFileExist(GetFilePathName(),prFileDialogInternal.puFilterManager.GetSelectedFilter().firstFilter)) // not existing => quit dialog
 | |
|         {
 | |
|           QuitFrame();
 | |
|           return true;
 | |
|         }
 | |
|         else // existing => confirm dialog to open
 | |
|         {
 | |
|           prFileDialogInternal.puIsOk = false;
 | |
|           prFileDialogInternal.puOkResultToConfirm = true;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       std::string name = std::string(OverWriteDialogTitleString) + "##" + prFileDialogInternal.puDLGtitle + prFileDialogInternal.puDLGkey + "OverWriteDialog";
 | |
| 
 | |
|       bool res = false;
 | |
| 
 | |
|       ImGui::OpenPopup(name.c_str());
 | |
|       if (ImGui::BeginPopupModal(name.c_str(), (bool*)0,
 | |
|         vFlags | ImGuiWindowFlags_AlwaysAutoResize |
 | |
|         ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
 | |
|       {
 | |
|         ImGui::SetWindowPos(prFileDialogInternal.puDialogCenterPos - ImGui::GetWindowSize() * 0.5f); // next frame needed for GetWindowSize to work
 | |
| 
 | |
|         ImGui::Text("%s", OverWriteDialogMessageString);
 | |
| 
 | |
|         if (IMGUI_BUTTON(OverWriteDialogConfirmButtonString))
 | |
|         {
 | |
|           prFileDialogInternal.puOkResultToConfirm = false;
 | |
|           prFileDialogInternal.puIsOk = true;
 | |
|           res = true;
 | |
|           ImGui::CloseCurrentPopup();
 | |
|         }
 | |
| 
 | |
|         ImGui::SameLine();
 | |
| 
 | |
|         if (IMGUI_BUTTON(OverWriteDialogCancelButtonString))
 | |
|         {
 | |
|           prFileDialogInternal.puOkResultToConfirm = false;
 | |
|           prFileDialogInternal.puIsOk = false;
 | |
|           res = false;
 | |
|           ImGui::CloseCurrentPopup();
 | |
|         }
 | |
| 
 | |
|         ImGui::EndPopup();
 | |
|       }
 | |
| 
 | |
|       if (res)
 | |
|       {
 | |
|         QuitFrame();
 | |
|       }
 | |
|       return res;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| #endif // __cplusplus
 | |
| 
 | |
| /////////////////////////////////////////////////////////////////
 | |
| ///// C Interface ///////////////////////////////////////////////
 | |
| /////////////////////////////////////////////////////////////////
 | |
| 
 | |
| // Return an initialized IGFD_Selection_Pair
 | |
| IMGUIFILEDIALOG_API IGFD_Selection_Pair IGFD_Selection_Pair_Get(void)
 | |
| {
 | |
|   IGFD_Selection_Pair res = {};
 | |
|   res.fileName = nullptr;
 | |
|   res.filePathName = nullptr;
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| // destroy only the content of vSelection_Pair
 | |
| IMGUIFILEDIALOG_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair* vSelection_Pair)
 | |
| {
 | |
|   if (vSelection_Pair)
 | |
|   {
 | |
|     delete[] vSelection_Pair->fileName;
 | |
|     delete[] vSelection_Pair->filePathName;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Return an initialized IGFD_Selection
 | |
| IMGUIFILEDIALOG_API IGFD_Selection IGFD_Selection_Get(void)
 | |
| {
 | |
|   return { nullptr, 0U };
 | |
| }
 | |
| 
 | |
| // destroy only the content of vSelection
 | |
| IMGUIFILEDIALOG_API void IGFD_Selection_DestroyContent(IGFD_Selection* vSelection)
 | |
| {
 | |
|   if (vSelection)
 | |
|   {
 | |
|     if (vSelection->table)
 | |
|     {
 | |
|       for (size_t i = 0U; i < vSelection->count; i++)
 | |
|       {
 | |
|         IGFD_Selection_Pair_DestroyContent(&vSelection->table[i]);
 | |
|       }
 | |
|       delete[] vSelection->table;
 | |
|     }
 | |
|     vSelection->count = 0U;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // create an instance of ImGuiFileDialog
 | |
| IMGUIFILEDIALOG_API ImGuiFileDialog* IGFD_Create(void)
 | |
| {
 | |
|   return new ImGuiFileDialog();
 | |
| }
 | |
| 
 | |
| // destroy the instance of ImGuiFileDialog
 | |
| IMGUIFILEDIALOG_API void IGFD_Destroy(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     delete vContext;
 | |
|     vContext = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // standard dialog
 | |
| IMGUIFILEDIALOG_API void IGFD_OpenDialog(
 | |
|   ImGuiFileDialog* vContext,
 | |
|   const char* vKey,
 | |
|   const char* vTitle,
 | |
|   const char* vFilters,
 | |
|   const char* vPath,
 | |
|   const char* vFileName,
 | |
|   const int vCountSelectionMax,
 | |
|   void* vUserDatas,
 | |
|   ImGuiFileDialogFlags flags)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->OpenDialog(
 | |
|       vKey, vTitle, vFilters, vPath, vFileName,
 | |
|       vCountSelectionMax, vUserDatas, flags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_OpenDialog2(
 | |
|   ImGuiFileDialog* vContext,
 | |
|   const char* vKey,
 | |
|   const char* vTitle,
 | |
|   const char* vFilters,
 | |
|   const char* vFilePathName,
 | |
|   const int vCountSelectionMax,
 | |
|   void* vUserDatas,
 | |
|   ImGuiFileDialogFlags flags)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->OpenDialog(
 | |
|       vKey, vTitle, vFilters, vFilePathName,
 | |
|       vCountSelectionMax, vUserDatas, flags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog(
 | |
|   ImGuiFileDialog* vContext,
 | |
|   const char* vKey,
 | |
|   const char* vTitle,
 | |
|   const char* vFilters,
 | |
|   const char* vPath,
 | |
|   const char* vFileName,
 | |
|   IGFD_PaneFun vSidePane,
 | |
|   const float vSidePaneWidth,
 | |
|   const int vCountSelectionMax,
 | |
|   void* vUserDatas,
 | |
|   ImGuiFileDialogFlags flags)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->OpenDialog(
 | |
|       vKey, vTitle, vFilters,
 | |
|       vPath, vFileName,
 | |
|       vSidePane, vSidePaneWidth,
 | |
|       vCountSelectionMax, vUserDatas, flags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog2(
 | |
|   ImGuiFileDialog* vContext,
 | |
|   const char* vKey,
 | |
|   const char* vTitle,
 | |
|   const char* vFilters,
 | |
|   const char* vFilePathName,
 | |
|   IGFD_PaneFun vSidePane,
 | |
|   const float vSidePaneWidth,
 | |
|   const int vCountSelectionMax,
 | |
|   void* vUserDatas,
 | |
|   ImGuiFileDialogFlags flags)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->OpenDialog(
 | |
|       vKey, vTitle, vFilters,
 | |
|       vFilePathName,
 | |
|       vSidePane, vSidePaneWidth,
 | |
|       vCountSelectionMax, vUserDatas, flags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // modal dialog
 | |
| IMGUIFILEDIALOG_API void IGFD_OpenModal(
 | |
|   ImGuiFileDialog* vContext,
 | |
|   const char* vKey,
 | |
|   const char* vTitle,
 | |
|   const char* vFilters,
 | |
|   const char* vPath,
 | |
|   const char* vFileName,
 | |
|   const int vCountSelectionMax,
 | |
|   void* vUserDatas,
 | |
|   ImGuiFileDialogFlags flags)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->OpenModal(
 | |
|       vKey, vTitle, vFilters, vPath, vFileName,
 | |
|       vCountSelectionMax, vUserDatas, flags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_OpenModal2(
 | |
|   ImGuiFileDialog* vContext,
 | |
|   const char* vKey,
 | |
|   const char* vTitle,
 | |
|   const char* vFilters,
 | |
|   const char* vFilePathName,
 | |
|   const int vCountSelectionMax,
 | |
|   void* vUserDatas,
 | |
|   ImGuiFileDialogFlags flags)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->OpenModal(
 | |
|       vKey, vTitle, vFilters, vFilePathName,
 | |
|       vCountSelectionMax, vUserDatas, flags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_OpenPaneModal(
 | |
|   ImGuiFileDialog* vContext,
 | |
|   const char* vKey,
 | |
|   const char* vTitle,
 | |
|   const char* vFilters,
 | |
|   const char* vPath,
 | |
|   const char* vFileName,
 | |
|   IGFD_PaneFun vSidePane,
 | |
|   const float vSidePaneWidth,
 | |
|   const int vCountSelectionMax,
 | |
|   void* vUserDatas,
 | |
|   ImGuiFileDialogFlags flags)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->OpenModal(
 | |
|       vKey, vTitle, vFilters,
 | |
|       vPath, vFileName,
 | |
|       vSidePane, vSidePaneWidth,
 | |
|       vCountSelectionMax, vUserDatas, flags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_OpenPaneModal2(
 | |
|   ImGuiFileDialog* vContext,
 | |
|   const char* vKey,
 | |
|   const char* vTitle,
 | |
|   const char* vFilters,
 | |
|   const char* vFilePathName,
 | |
|   IGFD_PaneFun vSidePane,
 | |
|   const float vSidePaneWidth,
 | |
|   const int vCountSelectionMax,
 | |
|   void* vUserDatas,
 | |
|   ImGuiFileDialogFlags flags)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->OpenModal(
 | |
|       vKey, vTitle, vFilters,
 | |
|       vFilePathName,
 | |
|       vSidePane, vSidePaneWidth,
 | |
|       vCountSelectionMax, vUserDatas, flags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API bool IGFD_DisplayDialog(ImGuiFileDialog* vContext,
 | |
|   const char* vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     return vContext->Display(vKey, vFlags, vMinSize, vMaxSize);
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_CloseDialog(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->Close();
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API bool IGFD_IsOk(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     return vContext->IsOk();
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API bool IGFD_WasKeyOpenedThisFrame(ImGuiFileDialog* vContext,
 | |
|   const char* vKey)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->WasOpenedThisFrame(vKey);
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API bool IGFD_WasOpenedThisFrame(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->WasOpenedThisFrame();
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API bool IGFD_IsKeyOpened(ImGuiFileDialog* vContext,
 | |
|   const char* vCurrentOpenedKey)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->IsOpened(vCurrentOpenedKey);
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API bool IGFD_IsOpened(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->IsOpened();
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API IGFD_Selection IGFD_GetSelection(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   IGFD_Selection res = IGFD_Selection_Get();
 | |
| 
 | |
|   if (vContext)
 | |
|   {
 | |
|     auto sel = vContext->GetSelection();
 | |
|     if (!sel.empty())
 | |
|     {
 | |
|       res.count = sel.size();
 | |
|       res.table = new IGFD_Selection_Pair[res.count];
 | |
| 
 | |
|       size_t idx = 0U;
 | |
|       for (const auto& s : sel)
 | |
|       {
 | |
|         IGFD_Selection_Pair* pair = res.table + idx++;
 | |
| 
 | |
|         // fileNameExt
 | |
|         if (!s.first.empty())
 | |
|         {
 | |
|           size_t siz = s.first.size() + 1U;
 | |
|           pair->fileName = new char[siz];
 | |
| #ifndef MSVC
 | |
|           strncpy(pair->fileName, s.first.c_str(), siz);
 | |
| #else
 | |
|           strncpy_s(pair->fileName, siz, s.first.c_str(), siz);
 | |
| #endif
 | |
|           pair->fileName[siz - 1U] = '\0';
 | |
|         }
 | |
| 
 | |
|         // filePathName
 | |
|         if (!s.second.empty())
 | |
|         {
 | |
|           size_t siz = s.first.size() + 1U;
 | |
|           pair->filePathName = new char[siz];
 | |
| #ifndef MSVC
 | |
|           strncpy(pair->filePathName, s.first.c_str(), siz);
 | |
| #else
 | |
|           strncpy_s(pair->filePathName, siz, s.first.c_str(), siz);
 | |
| #endif
 | |
|           pair->filePathName[siz - 1U] = '\0';
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return res;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API char* IGFD_GetFilePathName(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   char* res = nullptr;
 | |
| 
 | |
|   if (vContext)
 | |
|   {
 | |
|     auto s = vContext->GetFilePathName();
 | |
|     if (!s.empty())
 | |
|     {
 | |
|       size_t siz = s.size() + 1U;
 | |
|       res = new char[siz];
 | |
| #ifndef MSVC
 | |
|       strncpy(res, s.c_str(), siz);
 | |
| #else
 | |
|       strncpy_s(res, siz, s.c_str(), siz);
 | |
| #endif
 | |
|       res[siz - 1U] = '\0';
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API char* IGFD_GetCurrentFileName(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   char* res = nullptr;
 | |
| 
 | |
|   if (vContext)
 | |
|   {
 | |
|     auto s = vContext->GetCurrentFileName();
 | |
|     if (!s.empty())
 | |
|     {
 | |
|       size_t siz = s.size() + 1U;
 | |
|       res = new char[siz];
 | |
| #ifndef MSVC
 | |
|       strncpy(res, s.c_str(), siz);
 | |
| #else
 | |
|       strncpy_s(res, siz, s.c_str(), siz);
 | |
| #endif
 | |
|       res[siz - 1U] = '\0';
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API char* IGFD_GetCurrentPath(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   char* res = nullptr;
 | |
| 
 | |
|   if (vContext)
 | |
|   {
 | |
|     auto s = vContext->GetCurrentPath();
 | |
|     if (!s.empty())
 | |
|     {
 | |
|       size_t siz = s.size() + 1U;
 | |
|       res = new char[siz];
 | |
| #ifndef MSVC
 | |
|       strncpy(res, s.c_str(), siz);
 | |
| #else
 | |
|       strncpy_s(res, siz, s.c_str(), siz);
 | |
| #endif
 | |
|       res[siz - 1U] = '\0';
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API char* IGFD_GetCurrentFilter(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   char* res = nullptr;
 | |
| 
 | |
|   if (vContext)
 | |
|   {
 | |
|     auto s = vContext->GetCurrentFilter();
 | |
|     if (!s.empty())
 | |
|     {
 | |
|       size_t siz = s.size() + 1U;
 | |
|       res = new char[siz];
 | |
| #ifndef MSVC
 | |
|       strncpy(res, s.c_str(), siz);
 | |
| #else
 | |
|       strncpy_s(res, siz, s.c_str(), siz);
 | |
| #endif
 | |
|       res[siz - 1U] = '\0';
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void* IGFD_GetUserDatas(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     return vContext->GetUserDatas();
 | |
|   }
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_SetFileStyle(ImGuiFileDialog* vContext,
 | |
|   IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4 vColor, const char* vIcon, ImFont* vFont) //-V813
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_SetFileStyle2(ImGuiFileDialog* vContext,
 | |
|   IGFD_FileStyleFlags vFlags, const char* vCriteria, float vR, float vG, float vB, float vA, const char* vIcon, ImFont* vFont)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->SetFileStyle(vFlags, vCriteria, ImVec4(vR, vG, vB, vA), vIcon, vFont);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API bool IGFD_GetFileStyle(ImGuiFileDialog* vContext,
 | |
|   IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4* vOutColor, char** vOutIcon, ImFont** vOutFont)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     std::string icon;
 | |
|     bool res = vContext->GetFileStyle(vFlags, vCriteria, vOutColor, &icon, vOutFont);
 | |
|     if (!icon.empty() && vOutIcon)
 | |
|     {
 | |
|       size_t siz = icon.size() + 1U;
 | |
|       *vOutIcon = new char[siz];
 | |
| #ifndef MSVC
 | |
|       strncpy(*vOutIcon, icon.c_str(), siz);
 | |
| #else
 | |
|       strncpy_s(*vOutIcon, siz, icon.c_str(), siz);
 | |
| #endif
 | |
|       (*vOutIcon)[siz - 1U] = '\0';
 | |
|     }
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_ClearFilesStyle(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->ClearFilesStyle();
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void SetLocales(ImGuiFileDialog* vContext, const int vCategory, const char* vBeginLocale, const char* vEndLocale)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->SetLocales(vCategory, (vBeginLocale ? vBeginLocale : ""), (vEndLocale ? vEndLocale : ""));
 | |
|   }
 | |
| }
 | |
| 
 | |
| #ifdef USE_EXPLORATION_BY_KEYS
 | |
| IMGUIFILEDIALOG_API void IGFD_SetFlashingAttenuationInSeconds(ImGuiFileDialog* vContext, float vAttenValue)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->SetFlashingAttenuationInSeconds(vAttenValue);
 | |
|   }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef USE_BOOKMARK
 | |
| IMGUIFILEDIALOG_API char* IGFD_SerializeBookmarks(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   char* res = nullptr;
 | |
| 
 | |
|   if (vContext)
 | |
|   {
 | |
|     auto s = vContext->SerializeBookmarks();
 | |
|     if (!s.empty())
 | |
|     {
 | |
|       size_t siz = s.size() + 1U;
 | |
|       res = new char[siz];
 | |
| #ifndef MSVC
 | |
|       strncpy(res, s.c_str(), siz);
 | |
| #else
 | |
|       strncpy_s(res, siz, s.c_str(), siz);
 | |
| #endif
 | |
|       res[siz - 1U] = '\0';
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void IGFD_DeserializeBookmarks(ImGuiFileDialog* vContext, const char* vBookmarks)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->DeserializeBookmarks(vBookmarks);
 | |
|   }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef USE_THUMBNAILS
 | |
| IMGUIFILEDIALOG_API void SetCreateThumbnailCallback(ImGuiFileDialog* vContext, const IGFD_CreateThumbnailFun vCreateThumbnailFun)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->SetCreateThumbnailCallback(vCreateThumbnailFun);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void SetDestroyThumbnailCallback(ImGuiFileDialog* vContext, const IGFD_DestroyThumbnailFun vDestroyThumbnailFun)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->SetDestroyThumbnailCallback(vDestroyThumbnailFun);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IMGUIFILEDIALOG_API void ManageGPUThumbnails(ImGuiFileDialog* vContext)
 | |
| {
 | |
|   if (vContext)
 | |
|   {
 | |
|     vContext->ManageGPUThumbnails();
 | |
|   }
 | |
| }
 | |
| #endif // USE_THUMBNAILS
 | 
