From bd4297d2ad0680dbac67389a4c48665f9f9501db Mon Sep 17 00:00:00 2001 From: Alan Steremberg Date: Mon, 28 Jun 2021 16:18:06 -0400 Subject: [PATCH 1/4] added file open --- build.sh | 6 +- src/os.c | 4 +- verilator/Makefile | 6 +- verilator/imgui.ini | 13 +- verilator/sim/imgui/ImGuiFileDialog.cpp | 3503 +++++++++++++++++++ verilator/sim/imgui/ImGuiFileDialog.h | 1119 ++++++ verilator/sim/imgui/ImGuiFileDialogConfig.h | 70 + verilator/sim/imgui/dirent/ChangeLog | 129 + verilator/sim/imgui/dirent/LICENSE | 21 + verilator/sim/imgui/dirent/README.md | 96 + verilator/sim/imgui/dirent/dirent.h | 1160 ++++++ verilator/sim/sim_bus.cpp | 6 +- verilator/sim_main.cpp | 21 +- 13 files changed, 6140 insertions(+), 14 deletions(-) mode change 100644 => 100755 build.sh create mode 100644 verilator/sim/imgui/ImGuiFileDialog.cpp create mode 100644 verilator/sim/imgui/ImGuiFileDialog.h create mode 100644 verilator/sim/imgui/ImGuiFileDialogConfig.h create mode 100644 verilator/sim/imgui/dirent/ChangeLog create mode 100644 verilator/sim/imgui/dirent/LICENSE create mode 100644 verilator/sim/imgui/dirent/README.md create mode 100644 verilator/sim/imgui/dirent/dirent.h diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index e7cab8b..638953c --- a/build.sh +++ b/build.sh @@ -5,9 +5,9 @@ make all cd .. # Verilate HDL -cd verilator -./verilate.sh -cd .. +#cd verilator +#./verilate.sh +#cd .. # Hexify roms od -An -t x1 -v src/os.bin > verilator/rom.hex diff --git a/src/os.c b/src/os.c index cab4da9..c12a3af 100644 --- a/src/os.c +++ b/src/os.c @@ -20,7 +20,7 @@ void page_inputs() char label[5]; for (unsigned char j = 0; j < 6; j++) { - sprintf(label, "JOY%d", j + 1); + sprintf(label, "AOY%d", j + 1); write_string(label, 0xFF - (j * 2), 2, 4 + j); sprintf(label, "PAD%d", j + 1); @@ -435,4 +435,4 @@ void main() hsync_last = hsync; vsync_last = vsync; } -} \ No newline at end of file +} diff --git a/verilator/Makefile b/verilator/Makefile index fe626ee..352d8c1 100644 --- a/verilator/Makefile +++ b/verilator/Makefile @@ -57,8 +57,8 @@ V_SRC = \ $(RTL)/system.v \ $(RTL)/dpram.v \ $(RTL)/spram.v \ - $(RTL)/JTFRAME/jtframe_vtimer.v - $(RTL)/JTFRAME/jtframe_cen24.v + $(RTL)/JTFRAME/jtframe_vtimer.v \ + $(RTL)/JTFRAME/jtframe_cen24.v \ ../rtl/tv80/tv80_core.v \ ../rtl/tv80/tv80_alu.v \ ../rtl/tv80/tv80_mcode.v \ @@ -69,7 +69,7 @@ V_SRC = \ C_SRC = \ sim_main.cpp \ sim/sim_bus.cpp sim/sim_clock.cpp sim/sim_console.cpp sim/sim_video.cpp sim/sim_input.cpp \ - sim/imgui/imgui_impl_sdl.cpp sim/imgui/imgui_impl_opengl2.cpp sim/imgui/imgui_draw.cpp sim/imgui/imgui_widgets.cpp sim/imgui/imgui_tables.cpp sim/imgui/imgui.cpp + sim/imgui/imgui_impl_sdl.cpp sim/imgui/imgui_impl_opengl2.cpp sim/imgui/imgui_draw.cpp sim/imgui/imgui_widgets.cpp sim/imgui/imgui_tables.cpp sim/imgui/imgui.cpp sim/imgui/ImGuiFileDialog.cpp VOUT = obj_dir/Vtop.cpp all: $(EXE) diff --git a/verilator/imgui.ini b/verilator/imgui.ini index 923d2c5..626cc09 100644 --- a/verilator/imgui.ini +++ b/verilator/imgui.ini @@ -29,7 +29,7 @@ Size=560,393 Collapsed=0 [Window][CPU Registers] -Pos=1308,461 +Pos=1261,461 Size=269,188 Collapsed=0 @@ -39,7 +39,7 @@ Size=554,229 Collapsed=0 [Window][CHROM Editor] -Pos=19,866 +Pos=19,701 Size=553,169 Collapsed=0 @@ -58,3 +58,12 @@ Pos=-208,137 Size=738,454 Collapsed=0 +[Window][Choose File##ChooseFileDlgKey] +Pos=60,60 +Size=639,241 +Collapsed=0 + +[Table][0x5E7B4F09,4] +RefScale=13 +Column 0 Sort=0v + diff --git a/verilator/sim/imgui/ImGuiFileDialog.cpp b/verilator/sim/imgui/ImGuiFileDialog.cpp new file mode 100644 index 0000000..462a5e3 --- /dev/null +++ b/verilator/sim/imgui/ImGuiFileDialog.cpp @@ -0,0 +1,3503 @@ +// 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. +*/ + +#include "ImGuiFileDialog.h" + +#ifdef __cplusplus + +#include "imgui.h" + +#include +#include // stricmp / strcasecmp +#include +#include +#include +#include +#include +#include +#include +#if defined (__EMSCRIPTEN__) // EMSCRIPTEN +#include +#endif // EMSCRIPTEN +#if defined(__WIN32__) || defined(_WIN32) +#ifndef WIN32 +#define WIN32 +#endif // WIN32 +#define stat _stat +#define stricmp _stricmp +#include +#include "dirent/dirent.h" // directly open the dirent file attached to this lib +#define PATH_SEP '\\' +#ifndef PATH_MAX +#define PATH_MAX 260 +#endif // PATH_MAX +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined (__EMSCRIPTEN__) +#define UNIX +#define stricmp strcasecmp +#include +#include +#define PATH_SEP '/' +#endif // defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) + +#include "imgui.h" +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif // IMGUI_DEFINE_MATH_OPERATORS +#include "imgui_internal.h" + +#include +#include +#include + +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 + +// for lets you define your button widget +// if you have like me a special bi-color button +#ifndef IMGUI_PATH_BUTTON +#define IMGUI_PATH_BUTTON ImGui::Button +#endif // IMGUI_PATH_BUTTON +#ifndef IMGUI_BUTTON +#define IMGUI_BUTTON ImGui::Button +#endif // IMGUI_BUTTON + +// locales +#ifndef createDirButtonString +#define createDirButtonString "+" +#endif // createDirButtonString +#ifndef okButtonString +#define okButtonString "OK" +#endif // okButtonString +#ifndef cancelButtonString +#define cancelButtonString "Cancel" +#endif // cancelButtonString +#ifndef resetButtonString +#define resetButtonString "R" +#endif // resetButtonString +#ifndef drivesButtonString +#define drivesButtonString "Drives" +#endif // drivesButtonString +#ifndef searchString +#define searchString "Search :" +#endif // searchString +#ifndef dirEntryString +#define dirEntryString "[Dir]" +#endif // dirEntryString +#ifndef linkEntryString +#define linkEntryString "[Link]" +#endif // linkEntryString +#ifndef fileEntryString +#define fileEntryString "[File]" +#endif // fileEntryString +#ifndef fileNameString +#define fileNameString "File Name :" +#endif // fileNameString +#ifndef dirNameString +#define dirNameString "Directory Path :" +#endif // dirNameString +#ifndef buttonResetSearchString +#define buttonResetSearchString "Reset search" +#endif // buttonResetSearchString +#ifndef buttonDriveString +#define buttonDriveString "Drives" +#endif // buttonDriveString +#ifndef buttonResetPathString +#define buttonResetPathString "Reset to current directory" +#endif // buttonResetPathString +#ifndef buttonCreateDirString +#define buttonCreateDirString "Create Directory" +#endif // buttonCreateDirString +#ifndef tableHeaderAscendingIcon +#define tableHeaderAscendingIcon "A|" +#endif // tableHeaderAscendingIcon +#ifndef tableHeaderDescendingIcon +#define tableHeaderDescendingIcon "D|" +#endif // tableHeaderDescendingIcon +#ifndef tableHeaderFileNameString +#define tableHeaderFileNameString "File name" +#endif // tableHeaderFileNameString +#ifndef tableHeaderFileTypeString +#define tableHeaderFileTypeString "Type" +#endif // tableHeaderFileTypeString +#ifndef tableHeaderFileSizeString +#define tableHeaderFileSizeString "Size" +#endif // tableHeaderFileSizeString +#ifndef tableHeaderFileDateString +#define tableHeaderFileDateString "Date" +#endif // tableHeaderFileDateString +#ifndef OverWriteDialogTitleString +#define OverWriteDialogTitleString "The file Already Exist !" +#endif // OverWriteDialogTitleString +#ifndef OverWriteDialogMessageString +#define OverWriteDialogMessageString "Would you like to OverWrite it ?" +#endif // OverWriteDialogMessageString +#ifndef OverWriteDialogConfirmButtonString +#define OverWriteDialogConfirmButtonString "Confirm" +#endif // OverWriteDialogConfirmButtonString +#ifndef OverWriteDialogCancelButtonString +#define OverWriteDialogCancelButtonString "Cancel" +#endif // OverWriteDialogCancelButtonString +// see strftime functionin for customize +#ifndef DateTimeFormat +#define DateTimeFormat "%Y/%m/%d %H:%M" +#endif // DateTimeFormat + +#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 ToggleButton(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 ToggleButton +#endif // IMGUI_TOGGLE_BUTTON +#endif // USE_BOOKMARK + + // https://github.com/ocornut/imgui/issues/1720 + bool Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size = -1.0f) + { + 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); + } + + static std::string s_fs_root = std::string(1u, PATH_SEP); + + inline int alphaSort(const struct dirent** a, const struct dirent** b) + { + return strcoll((*a)->d_name, (*b)->d_name); + } + +#ifdef WIN32 + inline bool 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; + } + + inline std::vector wsplitStringToVector(const std::wstring& text, char delimiter, bool pushEmpty) + { + std::vector 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; + } +#endif + + inline bool 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; + } + + inline std::vector splitStringToVector(const std::string& text, char delimiter, bool pushEmpty) + { + std::vector arr; + if (!text.empty()) + { + std::string::size_type start = 0; + std::string::size_type end = text.find(delimiter, start); + while (end != std::string::npos) + { + std::string 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::string token = text.substr(start); + if (!token.empty() || (token.empty() && pushEmpty)) //-V728 + arr.push_back(token); + } + return arr; + } + + inline std::vector GetDrivesList() + { + std::vector 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); + replaceString(var, "\\", ""); + res = splitStringToVector(var, '\0', false); + } +#endif // WIN32 + + return res; + } + + inline bool 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! + } + +#ifdef WIN32 + inline std::wstring wGetString(const char* str) + { + std::wstring ret; + size_t sz; + if (!dirent_mbstowcs_s(&sz, nullptr, 0, str, 0)) + { + ret.resize(sz); + dirent_mbstowcs_s(nullptr, (wchar_t*)ret.data(), sz, str, sz - 1); + } + return ret; + } +#endif + + inline bool CreateDirectoryIfNotExist(const std::string& name) + { + bool res = false; + + if (!name.empty()) + { + if (!IsDirectoryExist(name)) + { +#ifdef WIN32 + std::wstring wname = wGetString(name.c_str()); + 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 // defined(UNIX) + if (!res) { + std::cout << "Error creating directory " << name << std::endl; + } + } + } + + return res; + } + + struct PathStruct + { + std::string path; + std::string name; + std::string ext; + + bool isOk; + + PathStruct() + { + isOk = false; + } + }; + + inline PathStruct ParsePathFileName(const std::string& vPathFileName) + { + PathStruct res; + + if (!vPathFileName.empty()) + { + std::string pfn = vPathFileName; + std::string separator(1u, PATH_SEP); + replaceString(pfn, "\\", separator); + 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); + replaceString(res.name, "." + res.ext, ""); + } + } + + return res; + } + + inline void 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") + { + replaceString(st, "\n", ""); + 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'; + } + + inline void ResetBuffer(char* vBuffer) + { + vBuffer[0] = '\0'; + } + + inline void SetBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr) + { + ResetBuffer(vBuffer); + AppendToBuffer(vBuffer, vBufferLen, vStr); + } + + IGFD::FileDialog::FileDialog() + { + m_AnyWindowsHovered = false; + m_IsOk = false; + m_ShowDialog = false; + m_ShowDrives = false; + m_CreateDirectoryMode = false; + dlg_optionsPane = nullptr; + dlg_optionsPaneWidth = 250; + dlg_filters = ""; + dlg_userDatas = 0; +#ifdef USE_BOOKMARK + m_BookmarkPaneShown = false; + m_BookmarkWidth = defaultBookmarkPaneWith; +#endif // USE_BOOKMARK + } + + IGFD::FileDialog::~FileDialog() = default; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + ///// CUSTOM SELECTABLE (Flashing Support) /////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef USE_EXPLORATION_BY_KEYS + bool IGFD::FileDialog::FlashableSelectable(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, NULL, 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); + 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 explicitely 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 (size_arg.x == 0.0f || (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; + if (flags & ImGuiSelectableFlags_Disabled) + { + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; + 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; + + // 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_Disabled) { button_flags |= ImGuiButtonFlags_Disabled; } + if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } + if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } + + if (flags & ImGuiSelectableFlags_Disabled) + selected = false; + + const bool was_selected = selected; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + + // 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 + window->DC.LastItemStatusFlags |= 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(); + + if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); + RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); + if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); + + // Automatically close popups + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.CurrentItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) + CloseCurrentPopup(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + return pressed; + } +#endif // USE_EXPLORATION_BY_KEYS + + ////////////////////////////////////////////////////////////////////////////////////////////////// + ///// STANDARD DIALOG //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + // path and fileName 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) + { + if (m_ShowDialog) // if already opened, quit + return; + + dlg_key = vKey; + dlg_title = vTitle; + dlg_path = vPath; + dlg_userDatas = vUserDatas; + dlg_flags = vFlags; + dlg_optionsPane = nullptr; + dlg_optionsPaneWidth = 0.0f; + dlg_countSelectionMax = vCountSelectionMax; + dlg_modal = false; + dlg_defaultExt.clear(); + + ParseFilters(vFilters); + SetDefaultFileName(vFileName); + SetPath(m_CurrentPath); + + m_ShowDialog = true; // open dialog +#ifdef USE_BOOKMARK + m_BookmarkPaneShown = false; +#endif // USE_BOOKMARK + } + + // 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) + { + if (m_ShowDialog) // if already opened, quit + return; + + dlg_key = vKey; + dlg_title = vTitle; + + auto ps = ParsePathFileName(vFilePathName); + if (ps.isOk) + { + dlg_path = ps.path; + SetDefaultFileName(ps.name + "." + ps.ext); + m_SelectedFileNames.clear(); + m_SelectedFileNames.emplace(ps.name + "." + ps.ext); + dlg_defaultExt = "." + ps.ext; + } + else + { + dlg_path = "."; + SetDefaultFileName(""); + dlg_defaultExt.clear(); + } + + dlg_optionsPane = nullptr; + dlg_optionsPaneWidth = 0.0f; + dlg_userDatas = vUserDatas; + dlg_flags = vFlags; + dlg_countSelectionMax = vCountSelectionMax; //-V101 + dlg_modal = false; + + ParseFilters(vFilters); + SetSelectedFilterWithExt(dlg_defaultExt); + SetPath(m_CurrentPath); + + m_ShowDialog = true; +#ifdef USE_BOOKMARK + m_BookmarkPaneShown = false; +#endif // USE_BOOKMARK + } + + // with pane + // path and fileName 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) + { + if (m_ShowDialog) // if already opened, quit + return; + + dlg_key = vKey; + dlg_title = vTitle; + dlg_path = vPath; + dlg_userDatas = vUserDatas; + dlg_flags = vFlags; + dlg_optionsPane = vSidePane; + dlg_optionsPaneWidth = vSidePaneWidth; + dlg_countSelectionMax = vCountSelectionMax; + dlg_modal = false; + dlg_defaultExt.clear(); + + ParseFilters(vFilters); + SetDefaultFileName(vFileName); + SetPath(m_CurrentPath); + + m_ShowDialog = true; // open dialog +#ifdef USE_BOOKMARK + m_BookmarkPaneShown = false; +#endif // USE_BOOKMARK + } + + // 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) + { + if (m_ShowDialog) // if already opened, quit + return; + + dlg_key = vKey; + dlg_title = vTitle; + + auto ps = ParsePathFileName(vFilePathName); + if (ps.isOk) + { + dlg_path = ps.path; + SetDefaultFileName(ps.name + "." + ps.ext); + m_SelectedFileNames.clear(); + m_SelectedFileNames.emplace(ps.name + "." + ps.ext); + dlg_defaultExt = "." + ps.ext; + } + else + { + dlg_path = "."; + SetDefaultFileName(""); + dlg_defaultExt.clear(); + } + + dlg_optionsPane = vSidePane; + dlg_optionsPaneWidth = vSidePaneWidth; + dlg_userDatas = vUserDatas; + dlg_flags = vFlags; + dlg_countSelectionMax = vCountSelectionMax; //-V101 + dlg_modal = false; + + ParseFilters(vFilters); + SetSelectedFilterWithExt(dlg_defaultExt); + SetPath(m_CurrentPath); + + m_ShowDialog = true; +#ifdef USE_BOOKMARK + m_BookmarkPaneShown = false; +#endif // USE_BOOKMARK + } + + ////////////////////////////////////////////////////////////////////////////////////////////////// + ///// 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) + { + if (m_ShowDialog) // if already opened, quit + return; + + OpenDialog( + vKey, vTitle, vFilters, + vPath, vFileName, + vCountSelectionMax, vUserDatas, vFlags); + + dlg_modal = 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) + { + if (m_ShowDialog) // if already opened, quit + return; + + OpenDialog( + vKey, vTitle, vFilters, + vFilePathName, + vCountSelectionMax, vUserDatas, vFlags); + + dlg_modal = true; + } + + // with pane + // path and fileName 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) + { + if (m_ShowDialog) // if already opened, quit + return; + + OpenDialog( + vKey, vTitle, vFilters, + vPath, vFileName, + vSidePane, vSidePaneWidth, + vCountSelectionMax, vUserDatas, vFlags); + + dlg_modal = 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) + { + if (m_ShowDialog) // if already opened, quit + return; + + OpenDialog( + vKey, vTitle, vFilters, + vFilePathName, + vSidePane, vSidePaneWidth, + vCountSelectionMax, vUserDatas, vFlags); + + dlg_modal = true; + } + + ////////////////////////////////////////////////////////////////////////////////////////////////// + ///// MAIN FUNCTION ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////// + + bool IGFD::FileDialog::Display(const std::string& vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize) + { + if (m_ShowDialog && dlg_key == vKey) + { + bool res = false; + + // to be sure than only one dialog is displayed per frame + ImGuiContext& g = *GImGui; + if (g.FrameCount == m_LastImGuiFrameCount) // one instance was displayed this frame before for this key +> quit + return res; + m_LastImGuiFrameCount = g.FrameCount; // mark this instance as used this frame + + std::string name = dlg_title + "##" + dlg_key; + if (m_Name != name) + { + m_FileList.clear(); + m_CurrentPath_Decomposition.clear(); + } + + m_IsOk = false; // reset dialog result + m_WantToQuit = false; // reset var used for start the dialog quit process from anywhere + + ResetEvents(); + + ImGui::SetNextWindowSizeConstraints(vMinSize, vMaxSize); + + bool beg = false; + if (dlg_modal && + !m_OkResultToConfirm) // 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, + vFlags | ImGuiWindowFlags_NoScrollbar); + } + else + { + beg = ImGui::Begin(name.c_str(), (bool*)nullptr, vFlags | ImGuiWindowFlags_NoScrollbar); + } + if (beg) + { + m_Name = name; //-V820 + m_AnyWindowsHovered |= ImGui::IsWindowHovered(); + + if (dlg_path.empty()) dlg_path = "."; // defaut path is '.' + if (m_SelectedFilter.empty() && // no filter selected + !m_Filters.empty()) // filter exist + m_SelectedFilter = *m_Filters.begin(); // we take the first filter + + // init list of files + if (m_FileList.empty() && !m_ShowDrives) + { + replaceString(dlg_defaultFileName, dlg_path, ""); // local path + if (!dlg_defaultFileName.empty()) + { + SetDefaultFileName(dlg_defaultFileName); + SetSelectedFilterWithExt(dlg_defaultExt); + } + else if (dlg_filters.empty()) // directory mode + SetDefaultFileName("."); + ScanDir(dlg_path); + } + + // draw dialog parts + DrawHeader(); // bookmark, directory, path + DrawContent(); // bookmark, files view, side pane + res = DrawFooter(); // file field, filter combobox, ok/cancel buttons + + // for display in dialog center, the confirm to overwrite dlg + m_DialogCenterPos = 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 m_OkResultToConfirm under + if (dlg_modal && + !m_OkResultToConfirm) + ImGui::EndPopup(); + } + + // same things here regarding m_OkResultToConfirm + if (!dlg_modal || m_OkResultToConfirm) + ImGui::End(); + + // confirm the result and show the confirm to overwrite dialog if needed + return Confirm_Or_OpenOverWriteFileDialog_IfNeeded(res, vFlags); + } + + return false; + } + + void IGFD::FileDialog::ResetEvents() + { + // reset events + m_DrivesClicked = false; + m_PathClicked = false; + m_CanWeContinue = true; + } + + void IGFD::FileDialog::DrawHeader() + { +#ifdef USE_BOOKMARK + DrawBookMark(); + ImGui::SameLine(); +#endif // USE_BOOKMARK + DrawDirectoryCreation(); + DrawPathComposer(); + DrawSearchBar(); + } + + void IGFD::FileDialog::DrawContent() + { + ImVec2 size = ImGui::GetContentRegionAvail() - ImVec2(0.0f, m_FooterHeight); + +#ifdef USE_BOOKMARK + if (m_BookmarkPaneShown) + { + //size.x -= m_BookmarkWidth; + ImGui::PushID("##splitterbookmark"); + float otherWidth = size.x - m_BookmarkWidth; + Splitter(true, 4.0f, &m_BookmarkWidth, &otherWidth, 10.0f, 10.0f + dlg_optionsPaneWidth, size.y); + ImGui::PopID(); + size.x -= otherWidth; + DrawBookmarkPane(size); + ImGui::SameLine(); + } +#endif // USE_BOOKMARK + + size.x = ImGui::GetContentRegionAvail().x - dlg_optionsPaneWidth; + + if (dlg_optionsPane) + { + ImGui::PushID("##splittersidepane"); + Splitter(true, 4.0f, &size.x, &dlg_optionsPaneWidth, 10.0f, 10.0f, size.y); + ImGui::PopID(); + } + + DrawFileListView(size); + + if (dlg_optionsPane) + { + DrawSidePane(size.y); + } + } + + bool IGFD::FileDialog::DrawFooter() + { + float posY = ImGui::GetCursorPos().y; // height of last bar calc + + if (!dlg_filters.empty()) + ImGui::Text(fileNameString); + else // directory chooser + ImGui::Text(dirNameString); + + ImGui::SameLine(); + + // Input file fields + float width = ImGui::GetContentRegionAvail().x; + if (!dlg_filters.empty()) + width -= FILTER_COMBO_WIDTH; + ImGui::PushItemWidth(width); + ImGui::InputText("##FileName", FileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER); + ImGui::PopItemWidth(); + + // combobox of filters + if (!dlg_filters.empty()) + { + ImGui::SameLine(); + + bool needToApllyNewFilter = false; + + ImGui::PushItemWidth(FILTER_COMBO_WIDTH); + if (ImGui::BeginCombo("##Filters", m_SelectedFilter.filter.c_str(), ImGuiComboFlags_None)) + { + intptr_t i = 0; + for (auto filter : m_Filters) + { + const bool item_selected = (filter.filter == m_SelectedFilter.filter); + ImGui::PushID((void*)(intptr_t)i++); + if (ImGui::Selectable(filter.filter.c_str(), item_selected)) + { + m_SelectedFilter = filter; + needToApllyNewFilter = true; + } + ImGui::PopID(); + } + + ImGui::EndCombo(); + } + ImGui::PopItemWidth(); + + if (needToApllyNewFilter) + { + SetPath(m_CurrentPath); + } + } + + bool res = false; + + // OK Button + if (m_CanWeContinue && strlen(FileNameBuffer)) + { + if (IMGUI_BUTTON(okButtonString)) + { + m_IsOk = true; + res = true; + } + + ImGui::SameLine(); + } + + // Cancel Button + if (IMGUI_BUTTON(cancelButtonString)) + { + m_IsOk = false; + res = true; + } + + m_FooterHeight = ImGui::GetCursorPosY() - posY; + + if (m_WantToQuit && m_IsOk) + { + res = true; + } + + return res; + } +#ifdef USE_BOOKMARK + void IGFD::FileDialog::DrawBookMark() + { + IMGUI_TOGGLE_BUTTON(bookmarksButtonString, &m_BookmarkPaneShown); + + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(bookmarksButtonHelpString); + } +#endif // USE_BOOKMARK + + void IGFD::FileDialog::DrawDirectoryCreation() + { + if (dlg_flags & ImGuiFileDialogFlags_DisableCreateDirectoryButton) + return; + + if (IMGUI_BUTTON(createDirButtonString)) + { + if (!m_CreateDirectoryMode) + { + m_CreateDirectoryMode = true; + ResetBuffer(DirectoryNameBuffer); + } + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(buttonCreateDirString); + + if (m_CreateDirectoryMode) + { + ImGui::SameLine(); + + ImGui::PushItemWidth(100.0f); + ImGui::InputText("##DirectoryFileName", DirectoryNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER); + ImGui::PopItemWidth(); + + ImGui::SameLine(); + + if (IMGUI_BUTTON(okButtonString)) + { + std::string newDir = std::string(DirectoryNameBuffer); + if (CreateDir(newDir)) + { + SetPath(m_CurrentPath + PATH_SEP + newDir); + } + + m_CreateDirectoryMode = false; + } + + ImGui::SameLine(); + + if (IMGUI_BUTTON(cancelButtonString)) + { + m_CreateDirectoryMode = false; + } + } + + ImGui::SameLine(); + + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + + ImGui::SameLine(); + } + + void IGFD::FileDialog::DrawPathComposer() + { + if (IMGUI_BUTTON(resetButtonString)) + { + SetPath("."); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(buttonResetPathString); + +#ifdef WIN32 + ImGui::SameLine(); + + if (IMGUI_BUTTON(drivesButtonString)) + { + m_DrivesClicked = true; + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(buttonDriveString); +#endif // WIN32 + + ImGui::SameLine(); + + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + + // show current path + if (!m_CurrentPath_Decomposition.empty()) + { + ImGui::SameLine(); + + if (m_InputPathActivated) + { + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::InputText("##pathedition", InputPathBuffer, MAX_PATH_BUFFER_SIZE); + ImGui::PopItemWidth(); + } + else + { + int _id = 0; + for (auto itPathDecomp = m_CurrentPath_Decomposition.begin(); + itPathDecomp != m_CurrentPath_Decomposition.end(); ++itPathDecomp) + { + if (itPathDecomp != m_CurrentPath_Decomposition.begin()) + ImGui::SameLine(); + ImGui::PushID(_id++); + bool click = IMGUI_PATH_BUTTON((*itPathDecomp).c_str()); + ImGui::PopID(); + if (click) + { + m_CurrentPath = ComposeNewPath(itPathDecomp); + m_PathClicked = true; + break; + } + // activate input for path + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) + { + SetBuffer(InputPathBuffer, MAX_PATH_BUFFER_SIZE, ComposeNewPath(itPathDecomp)); + m_InputPathActivated = true; + break; + } + } + } + } + } + + void IGFD::FileDialog::DrawSearchBar() + { + // search field + if (IMGUI_BUTTON(resetButtonString "##BtnImGuiFileDialogSearchField")) + { + ResetBuffer(SearchBuffer); + searchTag.clear(); + ApplyFilteringOnFileList(); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip(buttonResetSearchString); + ImGui::SameLine(); + ImGui::Text(searchString); + ImGui::SameLine(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + bool edited = ImGui::InputText("##InputImGuiFileDialogSearchField", SearchBuffer, MAX_FILE_DIALOG_NAME_BUFFER); + ImGui::PopItemWidth(); + if (edited) + { + searchTag = SearchBuffer; + ApplyFilteringOnFileList(); + } + } + + void IGFD::FileDialog::DrawFileListView(ImVec2 vSize) + { + ImGui::BeginChild("##FileDialog_FileList", vSize); + + 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 + ; + if (ImGui::BeginTable("##fileTable", 4, flags, vSize)) + { + ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible + ImGui::TableSetupColumn(m_HeaderFileName.c_str(), ImGuiTableColumnFlags_WidthStretch, -1, 0); + ImGui::TableSetupColumn(m_HeaderFileType.c_str(), ImGuiTableColumnFlags_WidthFixed | ((dlg_flags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 1); + ImGui::TableSetupColumn(m_HeaderFileSize.c_str(), ImGuiTableColumnFlags_WidthFixed | ((dlg_flags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 2); + ImGui::TableSetupColumn(m_HeaderFileDate.c_str(), ImGuiTableColumnFlags_WidthFixed | ((dlg_flags & 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 && !m_FileList.empty()) + { + if (sorts_specs->Specs->ColumnUserID == 0) + SortFields(SortingFieldEnum::FIELD_FILENAME, true); + else if (sorts_specs->Specs->ColumnUserID == 1) + SortFields(SortingFieldEnum::FIELD_TYPE, true); + else if (sorts_specs->Specs->ColumnUserID == 2) + SortFields(SortingFieldEnum::FIELD_SIZE, true); + else //if (sorts_specs->Specs->ColumnUserID == 3) => alwayd true for the moment, to uncomment if we add a fourth column + SortFields(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++) + { + 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) + SortFields(SortingFieldEnum::FIELD_FILENAME, true); + else if (column == 1) + SortFields(SortingFieldEnum::FIELD_TYPE, true); + else if (column == 2) + SortFields(SortingFieldEnum::FIELD_SIZE, true); + else //if (column == 3) => alwayd true for the moment, to uncomment if we add a fourth column + SortFields(SortingFieldEnum::FIELD_DATE, true); + } + } +#endif // USE_CUSTOM_SORTING_ICON + + if (!m_FilteredFileList.empty()) + { + m_FileListClipper.Begin((int)m_FilteredFileList.size(), ImGui::GetTextLineHeightWithSpacing()); + while (m_FileListClipper.Step()) + { + for (int i = m_FileListClipper.DisplayStart; i < m_FileListClipper.DisplayEnd; i++) + { + if (i < 0) continue; + + const FileInfoStruct& infos = m_FilteredFileList[i]; + + ImVec4 c; + std::string icon; + bool showColor = GetExtentionInfos(infos.ext, &c, &icon); + if (showColor) + ImGui::PushStyleColor(ImGuiCol_Text, c); + + std::string str = " " + infos.fileName; + if (infos.type == 'd') str = dirEntryString + str; + else if (infos.type == 'l') str = linkEntryString + str; + else if (infos.type == 'f') + { + if (showColor && !icon.empty()) + str = icon + str; + else + str = fileEntryString + str; + } + bool selected = (m_SelectedFileNames.find(infos.fileName) != m_SelectedFileNames.end()); // found + + ImGui::TableNextRow(); + + bool needToBreakTheloop = false; + + if (ImGui::TableNextColumn()) // file name + { + needToBreakTheloop = SelectableItem(i, infos, selected, "%s", str.c_str()); + } + if (ImGui::TableNextColumn()) // file type + { + ImGui::Text("%s", infos.ext.c_str()); + } + if (ImGui::TableNextColumn()) // file size + { + if (infos.type != '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()); + } + + if (showColor) + ImGui::PopStyleColor(); + + if (needToBreakTheloop) + break; + } + } + m_FileListClipper.End(); + } + + if (m_InputPathActivated) + { + auto gio = ImGui::GetIO(); + if (ImGui::IsKeyReleased(gio.KeyMap[ImGuiKey_Enter])) + { + SetPath(std::string(InputPathBuffer)); + m_InputPathActivated = false; + } + if (ImGui::IsKeyReleased(gio.KeyMap[ImGuiKey_Escape])) + { + m_InputPathActivated = false; + } + } +#ifdef USE_EXPLORATION_BY_KEYS + else + { + LocateByInputKey(); + ExploreWithkeys(); + } +#endif // USE_EXPLORATION_BY_KEYS + ImGui::EndTable(); + } + // changement de repertoire + if (m_PathClicked) + { + SetPath(m_CurrentPath); + } + + if (m_DrivesClicked) + { + GetDrives(); + } + + ImGui::EndChild(); + } + + bool IGFD::FileDialog::SelectableItem(int vidx, const FileInfoStruct& vInfos, bool vSelected, const char* vFmt, ...) + { + static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick | + ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth; + + va_list args; + va_start(args, vFmt); + vsnprintf(VariadicBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFmt, args); + va_end(args); + +#ifdef USE_EXPLORATION_BY_KEYS + bool flashed = BeginFlashItem(vidx); + bool res = FlashableSelectable(VariadicBuffer, vSelected, selectableFlags, + flashed); + if (flashed) + EndFlashItem(); +#else // USE_EXPLORATION_BY_KEYS + (void)vidx; // remove a warnings ofr unused var + bool res = ImGui::Selectable(VariadicBuffer, vSelected, selectableFlags); +#endif // USE_EXPLORATION_BY_KEYS + if (res) + { + if (vInfos.type == 'd') + { + if (ImGui::IsMouseDoubleClicked(0)) // 0 -> left mouse button double click + { + m_PathClicked = SelectDirectory(vInfos); + return true; // needToBreakTheloop + } + else if (dlg_filters.empty()) // directory chooser + { + SelectFileName(vInfos); + } + } + else + { + SelectFileName(vInfos); + + if (ImGui::IsMouseDoubleClicked(0)) + { + m_WantToQuit = true; + m_IsOk = true; + } + } + } + + return false; + } + + void IGFD::FileDialog::DrawSidePane(float vHeight) + { + ImGui::SameLine(); + + ImGui::BeginChild("##FileTypes", ImVec2(0, vHeight)); + + dlg_optionsPane(m_SelectedFilter.filter.c_str(), dlg_userDatas, &m_CanWeContinue); + + ImGui::EndChild(); + } + + void IGFD::FileDialog::Close() + { + dlg_key.clear(); + m_ShowDialog = false; + } + + bool IGFD::FileDialog::WasOpenedThisFrame(const std::string& vKey) + { + bool res = m_ShowDialog && dlg_key == vKey; + if (res) + { + ImGuiContext& g = *GImGui; + res &= m_LastImGuiFrameCount == g.FrameCount; // return true if a dialog was displayed in this frame + } + return res; + } + + bool IGFD::FileDialog::WasOpenedThisFrame() + { + bool res = m_ShowDialog; + if (res) + { + ImGuiContext& g = *GImGui; + res &= m_LastImGuiFrameCount == g.FrameCount; // return true if a dialog was displayed in this frame + } + return res; + } + + bool IGFD::FileDialog::IsOpened(const std::string& vKey) + { + return (m_ShowDialog && dlg_key == vKey); + } + + bool IGFD::FileDialog::IsOpened() + { + return m_ShowDialog; + } + + std::string IGFD::FileDialog::GetOpenedKey() + { + if (m_ShowDialog) + return dlg_key; + return ""; + } + + std::string IGFD::FileDialog::GetFilePathName() + { + std::string result = GetCurrentPath(); + + std::string filename = GetCurrentFileName(); + if (!filename.empty()) + { +#ifdef UNIX + if (s_fs_root != result) +#endif // UNIX + result += PATH_SEP; + + result += filename; + } + + return result; + } + + std::string IGFD::FileDialog::GetCurrentPath() + { + std::string path = m_CurrentPath; + + if (dlg_filters.empty()) // if directory mode + { + std::string selectedDirectory = FileNameBuffer; + if (!selectedDirectory.empty() && selectedDirectory != ".") + { + if (path.empty()) + path = selectedDirectory; + else + path += PATH_SEP + selectedDirectory; + } + } + + return path; + } + + std::string IGFD::FileDialog::GetCurrentFileName() + { + if (!dlg_filters.empty()) // if not directory mode + { + std::string result = FileNameBuffer; + + // if not a collection we can replace the filter by the extention we want + if (m_SelectedFilter.collectionfilters.empty()) + { + if (m_SelectedFilter.filter.find('*') == std::string::npos && result != m_SelectedFilter.filter) + { + size_t lastPoint = result.find_last_of('.'); + if (lastPoint != std::string::npos) + { + result = result.substr(0, lastPoint); + } + + result += m_SelectedFilter.filter; + } + } + + return result; + } + + return ""; // directory mode + } + + std::string IGFD::FileDialog::GetCurrentFilter() + { + return m_SelectedFilter.filter; + } + + UserDatas IGFD::FileDialog::GetUserDatas() + { + return dlg_userDatas; + } + + bool IGFD::FileDialog::IsOk() + { + return m_IsOk; + } + + std::map IGFD::FileDialog::GetSelection() + { + std::map res; + + for (auto& it : m_SelectedFileNames) + { + std::string result = GetCurrentPath(); + +#ifdef UNIX + if (s_fs_root != result) +#endif // UNIX + result += PATH_SEP; + + result += it; + + res[it] = result; + } + + return res; + } + + void IGFD::FileDialog::SetExtentionInfos(const std::string& vFilter, const FileExtentionInfosStruct& vInfos) + { + m_FileExtentionInfos[vFilter] = vInfos; + } + + void IGFD::FileDialog::SetExtentionInfos(const std::string& vFilter, const ImVec4& vColor, const std::string& vIcon) + { + m_FileExtentionInfos[vFilter] = FileExtentionInfosStruct(vColor, vIcon); + } + + bool IGFD::FileDialog::GetExtentionInfos(const std::string& vFilter, ImVec4* vOutColor, std::string* vOutIcon) + { + if (vOutColor) + { + if (m_FileExtentionInfos.find(vFilter) != m_FileExtentionInfos.end()) // found + { + *vOutColor = m_FileExtentionInfos[vFilter].color; + if (vOutIcon) + { + *vOutIcon = m_FileExtentionInfos[vFilter].icon; + } + return true; + } + } + return false; + } + + void IGFD::FileDialog::ClearExtentionInfos() + { + m_FileExtentionInfos.clear(); + } + + void IGFD::FileDialog::SetDefaultFileName(const std::string& vFileName) + { + dlg_defaultFileName = vFileName; + SetBuffer(FileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFileName); + } + + bool IGFD::FileDialog::SelectDirectory(const FileInfoStruct& vInfos) + { + bool pathClick = false; + + if (vInfos.fileName == "..") + { + if (m_CurrentPath_Decomposition.size() > 1) + { + m_CurrentPath = ComposeNewPath(m_CurrentPath_Decomposition.end() - 2); + pathClick = true; + } + } + else + { + std::string newPath; + + if (m_ShowDrives) + { + newPath = vInfos.fileName + PATH_SEP; + } + else + { +#ifdef __linux__ + if (s_fs_root == m_CurrentPath) + newPath = m_CurrentPath + vInfos.fileName; + else +#endif // __minux__ + newPath = m_CurrentPath + PATH_SEP + vInfos.fileName; + } + + if (IsDirectoryExist(newPath)) + { + if (m_ShowDrives) + { + m_CurrentPath = vInfos.fileName; + s_fs_root = m_CurrentPath; + } + else + { + m_CurrentPath = newPath; //-V820 + } + pathClick = true; + } + } + + return pathClick; + } + + void IGFD::FileDialog::SelectFileName(const FileInfoStruct& vInfos) + { + if (ImGui::GetIO().KeyCtrl) + { + if (dlg_countSelectionMax == 0) // infinite selection + { + if (m_SelectedFileNames.find(vInfos.fileName) == m_SelectedFileNames.end()) // not found +> add + { + AddFileNameInSelection(vInfos.fileName, true); + } + else // found +> remove + { + RemoveFileNameInSelection(vInfos.fileName); + } + } + else // selection limited by size + { + if (m_SelectedFileNames.size() < dlg_countSelectionMax) + { + if (m_SelectedFileNames.find(vInfos.fileName) == m_SelectedFileNames.end()) // not found +> add + { + AddFileNameInSelection(vInfos.fileName, true); + } + else // found +> remove + { + RemoveFileNameInSelection(vInfos.fileName); + } + } + } + } + else if (ImGui::GetIO().KeyShift) + { + if (dlg_countSelectionMax != 1) + { + m_SelectedFileNames.clear(); + // we will iterate filelist and get the last selection after the start selection + bool startMultiSelection = false; + std::string fileNameToSelect = vInfos.fileName; + std::string savedLastSelectedFileName; // for invert selection mode + for (auto& it : m_FileList) + { + const FileInfoStruct& infos = it; + + bool canTake = true; + if (!searchTag.empty() && infos.fileName.find(searchTag) == std::string::npos) canTake = false; + if (canTake) // if not filtered, we will take files who are filtered by the dialog + { + if (infos.fileName == m_LastSelectedFileName) + { + startMultiSelection = true; + AddFileNameInSelection(m_LastSelectedFileName, false); + } + else if (startMultiSelection) + { + if (dlg_countSelectionMax == 0) // infinite selection + { + AddFileNameInSelection(infos.fileName, false); + } + else // selection limited by size + { + if (m_SelectedFileNames.size() < dlg_countSelectionMax) + { + AddFileNameInSelection(infos.fileName, false); + } + else + { + startMultiSelection = false; + if (!savedLastSelectedFileName.empty()) + m_LastSelectedFileName = savedLastSelectedFileName; + break; + } + } + } + + if (infos.fileName == fileNameToSelect) + { + if (!startMultiSelection) // we are before the last Selected FileName, so we must inverse + { + savedLastSelectedFileName = m_LastSelectedFileName; + m_LastSelectedFileName = fileNameToSelect; + fileNameToSelect = savedLastSelectedFileName; + startMultiSelection = true; + AddFileNameInSelection(m_LastSelectedFileName, false); + } + else + { + startMultiSelection = false; + if (!savedLastSelectedFileName.empty()) + m_LastSelectedFileName = savedLastSelectedFileName; + break; + } + } + } + } + } + } + else + { + m_SelectedFileNames.clear(); + ResetBuffer(FileNameBuffer); + AddFileNameInSelection(vInfos.fileName, true); + } + } + + void IGFD::FileDialog::RemoveFileNameInSelection(const std::string& vFileName) + { + m_SelectedFileNames.erase(vFileName); + + if (m_SelectedFileNames.size() == 1) + { + snprintf(FileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str()); + } + else + { + snprintf(FileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", m_SelectedFileNames.size()); + } + } + + void IGFD::FileDialog::AddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName) + { + m_SelectedFileNames.emplace(vFileName); + + if (m_SelectedFileNames.size() == 1) + { + snprintf(FileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str()); + } + else + { + snprintf(FileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", m_SelectedFileNames.size()); + } + + if (vSetLastSelectionFileName) + m_LastSelectedFileName = vFileName; + } + + void IGFD::FileDialog::SetPath(const std::string& vPath) + { + m_ShowDrives = false; + m_CurrentPath = vPath; + m_FileList.clear(); + m_CurrentPath_Decomposition.clear(); + if (dlg_filters.empty()) // directory mode + SetDefaultFileName("."); + ScanDir(m_CurrentPath); + } + + static std::string round_n(double vvalue, int n) + { + std::stringstream tmp; + tmp << std::setprecision(n) << std::fixed << vvalue; + return tmp.str(); + } + + static void FormatFileSize(size_t vByteSize, std::string* vFormat) + { + if (vFormat && vByteSize != 0) + { + static double lo = 1024.0; + static double ko = 1024.0 * 1024.0; + static double mo = 1024.0 * 1024.0 * 1024.0; + + double v = (double)vByteSize; + + if (v < lo) + *vFormat = round_n(v, 0) + " o"; // octet + else if (v < ko) + *vFormat = round_n(v / lo, 2) + " Ko"; // ko + else if (v < mo) + *vFormat = round_n(v / ko, 2) + " Mo"; // Mo + else + *vFormat = round_n(v / mo, 2) + " Go"; // Go + } + } + + void IGFD::FileDialog::CompleteFileInfos(FileInfoStruct* vFileInfoStruct) + { + if (vFileInfoStruct && + vFileInfoStruct->fileName != "." && + vFileInfoStruct->fileName != "..") + { + // _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 */ + + std::string fpn; + + if (vFileInfoStruct->type == 'f') // file + fpn = vFileInfoStruct->filePath + PATH_SEP + vFileInfoStruct->fileName; + else if (vFileInfoStruct->type == 'l') // link + fpn = vFileInfoStruct->filePath + PATH_SEP + vFileInfoStruct->fileName; + else if (vFileInfoStruct->type == 'd') // directory + fpn = vFileInfoStruct->filePath + PATH_SEP + vFileInfoStruct->fileName; + + struct stat statInfos; + char timebuf[100]; + int result = stat(fpn.c_str(), &statInfos); + if (!result) + { + if (vFileInfoStruct->type != 'd') + { + vFileInfoStruct->fileSize = (size_t)statInfos.st_size; + FormatFileSize(vFileInfoStruct->fileSize, + &vFileInfoStruct->formatedFileSize); + } + + size_t len = 0; +#ifdef MSVC + struct tm _tm; + errno_t err = localtime_s(&_tm, &statInfos.st_mtime); + if (!err) len = strftime(timebuf, 99, DateTimeFormat, &_tm); +#else // MSVC + struct tm* _tm = localtime(&statInfos.st_mtime); + if (_tm) len = strftime(timebuf, 99, DateTimeFormat, _tm); +#endif // MSVC + if (len) + { + vFileInfoStruct->fileModifDate = std::string(timebuf, len); + } + } + } + } + + void IGFD::FileDialog::SortFields(SortingFieldEnum vSortingField, bool vCanChangeOrder) + { + if (vSortingField != SortingFieldEnum::FIELD_NONE) + { + m_HeaderFileName = tableHeaderFileNameString; + m_HeaderFileType = tableHeaderFileTypeString; + m_HeaderFileSize = tableHeaderFileSizeString; + m_HeaderFileDate = tableHeaderFileDateString; + } + + if (vSortingField == SortingFieldEnum::FIELD_FILENAME) + { + if (vCanChangeOrder && m_SortingField == vSortingField) + m_SortingDirection[0] = !m_SortingDirection[0]; + + if (m_SortingDirection[0]) + { +#ifdef USE_CUSTOM_SORTING_ICON + m_HeaderFileName = tableHeaderDescendingIcon + m_HeaderFileName; +#endif // USE_CUSTOM_SORTING_ICON + std::sort(m_FileList.begin(), m_FileList.end(), + [](const FileInfoStruct& a, const FileInfoStruct& b) -> bool + { + if (a.fileName[0] == '.' && b.fileName[0] != '.') return true; + if (a.fileName[0] != '.' && b.fileName[0] == '.') return false; + if (a.fileName[0] == '.' && b.fileName[0] == '.') + { + if (a.fileName.length() == 1) return false; + if (b.fileName.length() == 1) return true; + return (stricmp(a.fileName.c_str() + 1, b.fileName.c_str() + 1) < 0); + } + + if (a.type != b.type) return (a.type == 'd'); // directory in first + return (stricmp(a.fileName.c_str(), b.fileName.c_str()) < 0); // sort in insensitive case + }); + } + else + { +#ifdef USE_CUSTOM_SORTING_ICON + m_HeaderFileName = tableHeaderAscendingIcon + m_HeaderFileName; +#endif // USE_CUSTOM_SORTING_ICON + std::sort(m_FileList.begin(), m_FileList.end(), + [](const FileInfoStruct& a, const FileInfoStruct& b) -> bool + { + if (a.fileName[0] == '.' && b.fileName[0] != '.') return false; + if (a.fileName[0] != '.' && b.fileName[0] == '.') return true; + if (a.fileName[0] == '.' && b.fileName[0] == '.') + { + if (a.fileName.length() == 1) return true; + if (b.fileName.length() == 1) return false; + return (stricmp(a.fileName.c_str() + 1, b.fileName.c_str() + 1) > 0); + } + + if (a.type != b.type) return (a.type != 'd'); // directory in last + return (stricmp(a.fileName.c_str(), b.fileName.c_str()) > 0); // sort in insensitive case + }); + } + } + else if (vSortingField == SortingFieldEnum::FIELD_TYPE) + { + if (vCanChangeOrder && m_SortingField == vSortingField) + m_SortingDirection[1] = !m_SortingDirection[1]; + + if (m_SortingDirection[1]) + { +#ifdef USE_CUSTOM_SORTING_ICON + m_HeaderFileType = tableHeaderDescendingIcon + m_HeaderFileType; +#endif // USE_CUSTOM_SORTING_ICON + std::sort(m_FileList.begin(), m_FileList.end(), + [](const FileInfoStruct& a, const FileInfoStruct& b) -> bool + { + if (a.type != b.type) return (a.type == 'd'); // directory in first + return (a.ext < b.ext); // else + }); + } + else + { +#ifdef USE_CUSTOM_SORTING_ICON + m_HeaderFileType = tableHeaderAscendingIcon + m_HeaderFileType; +#endif // USE_CUSTOM_SORTING_ICON + std::sort(m_FileList.begin(), m_FileList.end(), + [](const FileInfoStruct& a, const FileInfoStruct& b) -> bool + { + if (a.type != b.type) return (a.type != 'd'); // directory in last + return (a.ext > b.ext); // else + }); + } + } + else if (vSortingField == SortingFieldEnum::FIELD_SIZE) + { + if (vCanChangeOrder && m_SortingField == vSortingField) + m_SortingDirection[2] = !m_SortingDirection[2]; + + if (m_SortingDirection[2]) + { +#ifdef USE_CUSTOM_SORTING_ICON + m_HeaderFileSize = tableHeaderDescendingIcon + m_HeaderFileSize; +#endif // USE_CUSTOM_SORTING_ICON + std::sort(m_FileList.begin(), m_FileList.end(), + [](const FileInfoStruct& a, const FileInfoStruct& b) -> bool + { + if (a.type != b.type) return (a.type == 'd'); // directory in first + return (a.fileSize < b.fileSize); // else + }); + } + else + { +#ifdef USE_CUSTOM_SORTING_ICON + m_HeaderFileSize = tableHeaderAscendingIcon + m_HeaderFileSize; +#endif // USE_CUSTOM_SORTING_ICON + std::sort(m_FileList.begin(), m_FileList.end(), + [](const FileInfoStruct& a, const FileInfoStruct& b) -> bool + { + if (a.type != b.type) return (a.type != 'd'); // directory in last + return (a.fileSize > b.fileSize); // else + }); + } + } + else if (vSortingField == SortingFieldEnum::FIELD_DATE) + { + if (vCanChangeOrder && m_SortingField == vSortingField) + m_SortingDirection[3] = !m_SortingDirection[3]; + + if (m_SortingDirection[3]) + { +#ifdef USE_CUSTOM_SORTING_ICON + m_HeaderFileDate = tableHeaderDescendingIcon + m_HeaderFileDate; +#endif // USE_CUSTOM_SORTING_ICON + std::sort(m_FileList.begin(), m_FileList.end(), + [](const FileInfoStruct& a, const FileInfoStruct& b) -> bool + { + if (a.type != b.type) return (a.type == 'd'); // directory in first + return (a.fileModifDate < b.fileModifDate); // else + }); + } + else + { +#ifdef USE_CUSTOM_SORTING_ICON + m_HeaderFileDate = tableHeaderAscendingIcon + m_HeaderFileDate; +#endif // USE_CUSTOM_SORTING_ICON + std::sort(m_FileList.begin(), m_FileList.end(), + [](const FileInfoStruct& a, const FileInfoStruct& b) -> bool + { + if (a.type != b.type) return (a.type != 'd'); // directory in last + return (a.fileModifDate > b.fileModifDate); // else + }); + } + } + + if (vSortingField != SortingFieldEnum::FIELD_NONE) + { + m_SortingField = vSortingField; + } + + ApplyFilteringOnFileList(); + } + + void IGFD::FileDialog::ScanDir(const std::string& vPath) + { + struct dirent** files = nullptr; + int i = 0; + int n = 0; + std::string path = vPath; + + if (m_CurrentPath_Decomposition.empty()) + { + SetCurrentDir(path); + } + + if (!m_CurrentPath_Decomposition.empty()) + { +#ifdef WIN32 + if (path == s_fs_root) + path += PATH_SEP; +#endif // WIN32 + n = scandir(path.c_str(), &files, nullptr, alphaSort); + + m_FileList.clear(); + + if (n > 0) + { + for (i = 0; i < n; i++) + { + struct dirent* ent = files[i]; + + FileInfoStruct infos; + + infos.filePath = path; + infos.fileName = ent->d_name; + infos.fileName_optimized = OptimizeFilenameForSearchOperations(infos.fileName); + + if (infos.fileName.empty() || (infos.fileName == "." && !dlg_filters.empty())) continue; // filename empty or filename is the current dir '.' + if (infos.fileName != ".." && (dlg_flags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos.fileName[0] == '.') // dont show hidden files + if (!dlg_filters.empty() || (dlg_filters.empty() && infos.fileName != ".")) // except "." if in directory mode + continue; + + switch (ent->d_type) + { + case DT_REG: + infos.type = 'f'; break; + case DT_DIR: + infos.type = 'd'; break; + case DT_LNK: + infos.type = 'l'; break; + } + + if (infos.type == 'f' || + infos.type == 'l') // link can have the same extention of a file + { + size_t lpt = infos.fileName.find_last_of('.'); + if (lpt != std::string::npos) + { + infos.ext = infos.fileName.substr(lpt); + } + + if (!dlg_filters.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 (!m_SelectedFilter.empty() && // selected filter exist + (!m_SelectedFilter.filterExist(infos.ext) && // filter not found + m_SelectedFilter.filter != ".*")) + { + continue; + } + } + } + + CompleteFileInfos(&infos); + m_FileList.push_back(infos); + } + + for (i = 0; i < n; i++) + { + free(files[i]); + } + + free(files); + } + + SortFields(m_SortingField); + } + } + + void IGFD::FileDialog::SetCurrentDir(const std::string& vPath) + { + std::string path = vPath; +#ifdef WIN32 + if (s_fs_root == path) + path += PATH_SEP; +#endif // WIN32 + char real_path[PATH_MAX]; + 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 = wGetString(path.c_str()); + numchar = GetFullPathNameW(wpath.c_str(), 0, nullptr, nullptr); + std::wstring fpath(numchar, 0); + GetFullPathNameW(wpath.c_str(), numchar, (wchar_t*)fpath.data(), nullptr); + int error = dirent_wcstombs_s(nullptr, real_path, PATH_MAX, fpath.c_str(), PATH_MAX - 1); + if (error)numchar = 0; + if (!numchar) + { + std::cout << "fail to obtain FullPathName " << path << std::endl; + } +#elif defined(UNIX) // UNIX is LINUX or APPLE + char* numchar = realpath(path.c_str(), real_path); +#endif // defined(UNIX) + if (numchar != 0) + { + m_CurrentPath = real_path; + if (m_CurrentPath[m_CurrentPath.size() - 1] == PATH_SEP) + { + m_CurrentPath = m_CurrentPath.substr(0, m_CurrentPath.size() - 1); + } + SetBuffer(InputPathBuffer, MAX_PATH_BUFFER_SIZE, m_CurrentPath); + m_CurrentPath_Decomposition = splitStringToVector(m_CurrentPath, PATH_SEP, false); +#if defined(UNIX) // UNIX is LINUX or APPLE + m_CurrentPath_Decomposition.insert(m_CurrentPath_Decomposition.begin(), std::string(1u, PATH_SEP)); +#endif // defined(UNIX) + if (!m_CurrentPath_Decomposition.empty()) + { +#ifdef WIN32 + s_fs_root = m_CurrentPath_Decomposition[0]; +#endif // WIN32 + } + } + + closedir(dir); + } + } + + bool IGFD::FileDialog::CreateDir(const std::string& vPath) + { + bool res = false; + + if (!vPath.empty()) + { + std::string path = m_CurrentPath + PATH_SEP + vPath; + + res = CreateDirectoryIfNotExist(path); + } + + return res; + } + + std::string IGFD::FileDialog::ComposeNewPath(std::vector::iterator vIter) + { + std::string res; + + while (true) + { + if (!res.empty()) + { +#ifdef WIN32 + res = *vIter + PATH_SEP + res; +#elif defined(UNIX) // UNIX is LINUX or APPLE + if (*vIter == s_fs_root) + { + res = *vIter + res; + } + else + { + res = *vIter + PATH_SEP + res; + } +#endif // defined(UNIX) + } + else + { + res = *vIter; + } + + if (vIter == m_CurrentPath_Decomposition.begin()) + { +#if defined(UNIX) // UNIX is LINUX or APPLE + if (res[0] != PATH_SEP) + res = PATH_SEP + res; +#endif // defined(UNIX) + break; + } + + --vIter; + } + + return res; + } + + void IGFD::FileDialog::GetDrives() + { + auto drives = GetDrivesList(); + if (!drives.empty()) + { + m_CurrentPath.clear(); + m_CurrentPath_Decomposition.clear(); + m_FileList.clear(); + for (auto& drive : drives) + { + FileInfoStruct infos; + infos.fileName = drive; + infos.fileName_optimized = OptimizeFilenameForSearchOperations(drive); + infos.type = 'd'; + + if (!infos.fileName.empty()) + { + m_FileList.push_back(infos); + } + } + m_ShowDrives = true; + ApplyFilteringOnFileList(); + } + } + + void IGFD::FileDialog::ParseFilters(const char* vFilters) + { + m_Filters.clear(); + + if (vFilters) + dlg_filters = vFilters; // file mode + else + dlg_filters.clear(); // directtory mode + + if (!dlg_filters.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 = dlg_filters.find_first_of("{,", p)) != nan) + { + FilterInfosStruct infos; + + if (dlg_filters[p] == '{') // { + { + infos.filter = dlg_filters.substr(lp, p - lp); + p++; + lp = dlg_filters.find('}', p); + if (lp != nan) + { + std::string fs = dlg_filters.substr(p, lp - p); + auto arr = splitStringToVector(fs, ',', false); + for (auto a : arr) + { + infos.collectionfilters.emplace(a); + } + } + p = lp + 1; + } + else // , + { + infos.filter = dlg_filters.substr(lp, p - lp); + p++; + } + + if (!currentFilterFound && m_SelectedFilter.filter == infos.filter) + { + currentFilterFound = true; + m_SelectedFilter = infos; + } + + lp = p; + if (!infos.empty()) + m_Filters.emplace_back(infos); + } + + std::string token = dlg_filters.substr(lp); + if (!token.empty()) + { + FilterInfosStruct infos; + infos.filter = token; + m_Filters.emplace_back(infos); + } + + if (!currentFilterFound) + if (!m_Filters.empty()) + m_SelectedFilter = *m_Filters.begin(); + } + } + + void IGFD::FileDialog::SetSelectedFilterWithExt(const std::string& vFilter) + { + if (!m_Filters.empty()) + { + if (!vFilter.empty()) + { + // std::map + for (const auto& infos : m_Filters) + { + if (vFilter == infos.filter) + { + m_SelectedFilter = 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) + { + m_SelectedFilter = infos; + } + } + } + } + } + + if (m_SelectedFilter.empty()) + m_SelectedFilter = *m_Filters.begin(); + } + } + + std::string IGFD::FileDialog::OptimizeFilenameForSearchOperations(std::string vFileName) + { + // convert to lower case + for (char& c : vFileName) + c = (char)std::tolower(c); + return vFileName; + } + + void IGFD::FileDialog::ApplyFilteringOnFileList() + { + m_FilteredFileList.clear(); + + for (auto& it : m_FileList) + { + const FileInfoStruct& infos = it; + + bool show = true; + + // if search tag + if (!searchTag.empty() && + infos.fileName_optimized.find(searchTag) == std::string::npos && // first try wihtout case and accents + infos.fileName.find(searchTag) == std::string::npos) // second if searched with case and accents + { + show = false; + } + + if (dlg_filters.empty() && infos.type != 'd') // directory mode + { + show = false; + } + + if (show) + { + m_FilteredFileList.push_back(infos); + } + } + } + +#ifdef USE_EXPLORATION_BY_KEYS + + ////////////////////////////////////////////////////////////////////////////// + //// LOCATE / EXPLORE WITH KEYS ////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + bool IGFD::FileDialog::LocateItem_Loop(ImWchar vC) + { + bool found = false; + + for (size_t i =m_LocateFileByInputChar_lastFileIdx; i < m_FilteredFileList.size(); i++) + { + if (m_FilteredFileList[i].fileName_optimized[0] == vC || // lower case search + m_FilteredFileList[i].fileName[0] == vC) // maybe upper case search + { + //float p = ((float)i) * ImGui::GetTextLineHeightWithSpacing(); + float p = (float)((double)i / (double)m_FilteredFileList.size()) * ImGui::GetScrollMaxY(); + ImGui::SetScrollY(p); + m_LocateFileByInputChar_lastFound = true; + m_LocateFileByInputChar_lastFileIdx = i; + StartFlashItem(m_LocateFileByInputChar_lastFileIdx); + + auto infos = &m_FilteredFileList[m_LocateFileByInputChar_lastFileIdx]; + + if (infos->type == 'd') + { + if (dlg_filters.empty()) // directory chooser + { + SelectFileName(*infos); + } + } + else + { + SelectFileName(*infos); + } + + found = true; + + break; + } + } + + return found; + } + + void IGFD::FileDialog::LocateByInputKey() + { + ImGuiContext& g = *GImGui; + if (!g.ActiveId && !m_FilteredFileList.empty()) + { + auto& queueChar = ImGui::GetIO().InputQueueCharacters; + + // point by char + if (!queueChar.empty()) + { + ImWchar c = queueChar.back(); + if (m_LocateFileByInputChar_InputQueueCharactersSize != queueChar.size()) + { + if (c == m_LocateFileByInputChar_lastChar) // next file starting with same char until + { + if (m_LocateFileByInputChar_lastFileIdx < m_FilteredFileList.size() - 1) + m_LocateFileByInputChar_lastFileIdx++; + else + m_LocateFileByInputChar_lastFileIdx = 0; + } + + if (!LocateItem_Loop(c)) + { + // not found, loop again from 0 this time + m_LocateFileByInputChar_lastFileIdx = 0; + LocateItem_Loop(c); + } + + m_LocateFileByInputChar_lastChar = c; + } + } + + m_LocateFileByInputChar_InputQueueCharactersSize = queueChar.size(); + } + } + + void IGFD::FileDialog::ExploreWithkeys() + { + ImGuiContext& g = *GImGui; + if (!g.ActiveId && !m_FilteredFileList.empty()) + { + // explore + bool exploreByKey = false; + bool enterInDirectory = false; + bool exitDirectory = false; + if (ImGui::IsKeyPressed(IGFD_KEY_UP)) + { + exploreByKey = true; + if (m_LocateFileByInputChar_lastFileIdx > 0) + m_LocateFileByInputChar_lastFileIdx--; + } + else if (ImGui::IsKeyPressed(IGFD_KEY_DOWN)) + { + exploreByKey = true; + if (m_LocateFileByInputChar_lastFileIdx < m_FilteredFileList.size() - 1) + m_LocateFileByInputChar_lastFileIdx++; + } + else if (ImGui::IsKeyReleased(IGFD_KEY_ENTER) && ImGui::IsWindowHovered()) + { + exploreByKey = true; + enterInDirectory = true; + } + else if (ImGui::IsKeyReleased(IGFD_KEY_BACKSPACE) && ImGui::IsWindowHovered()) + { + exploreByKey = true; + exitDirectory = true; + } + + if (exploreByKey) + { + //float totalHeight = m_FilteredFileList.size() * ImGui::GetTextLineHeightWithSpacing(); + float p = (float)((double)m_LocateFileByInputChar_lastFileIdx / (double)m_FilteredFileList.size()) * ImGui::GetScrollMaxY();// seems not udpated in tables version outside tables + //float p = ((float)locateFileByInputChar_lastFileIdx) * ImGui::GetTextLineHeightWithSpacing(); + ImGui::SetScrollY(p); + StartFlashItem(m_LocateFileByInputChar_lastFileIdx); + + auto infos = &m_FilteredFileList[m_LocateFileByInputChar_lastFileIdx]; + + if (infos->type == 'd') + { + if (!dlg_filters.empty() || enterInDirectory) + { + if (enterInDirectory) + { + if (SelectDirectory(*infos)) + { + // changement de repertoire + SetPath(m_CurrentPath); + if (m_LocateFileByInputChar_lastFileIdx > m_FilteredFileList.size() - 1) + { + m_LocateFileByInputChar_lastFileIdx = 0; + } + } + } + } + else // directory chooser + { + SelectFileName(*infos); + } + } + else + { + SelectFileName(*infos); + } + + if (exitDirectory) + { + FileInfoStruct nfo; + nfo.fileName = ".."; + + if (SelectDirectory(nfo)) + { + // changement de repertoire + SetPath(m_CurrentPath); + if (m_LocateFileByInputChar_lastFileIdx > m_FilteredFileList.size() - 1) + { + m_LocateFileByInputChar_lastFileIdx = 0; + } + } +#ifdef WIN32 + else + { + if (m_CurrentPath_Decomposition.size() == 1) + { + GetDrives(); // display drives + } + } +#endif // WIN32 + } + } + } + } + + void IGFD::FileDialog::StartFlashItem(size_t vIdx) + { + m_FlashAlpha = 1.0f; + m_FlashedItem = vIdx; + } + + bool IGFD::FileDialog::BeginFlashItem(size_t vIdx) + { + bool res = false; + + if (m_FlashedItem == vIdx && + std::abs(m_FlashAlpha - 0.0f) > 0.00001f) + { + m_FlashAlpha -= m_FlashAlphaAttenInSecs * ImGui::GetIO().DeltaTime; + if (m_FlashAlpha < 0.0f) m_FlashAlpha = 0.0f; + + ImVec4 hov = ImGui::GetStyleColorVec4(ImGuiCol_HeaderHovered); + hov.w = m_FlashAlpha; + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, hov); + res = true; + } + + return res; + } + + void IGFD::FileDialog::EndFlashItem() + { + ImGui::PopStyleColor(); + } + + void IGFD::FileDialog::SetFlashingAttenuationInSeconds(float vAttenValue) + { + m_FlashAlphaAttenInSecs = 1.0f / ImMax(vAttenValue, 0.01f); + } +#endif // USE_EXPLORATION_BY_KEYS + +#ifdef USE_BOOKMARK + + ////////////////////////////////////////////////////////////////////////////// + //// BOOKMARK FEATURE //////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + void IGFD::FileDialog::DrawBookmarkPane(ImVec2 vSize) + { + ImGui::BeginChild("##bookmarkpane", vSize); + + static int selectedBookmarkForEdition = -1; + + if (IMGUI_BUTTON(addBookmarkButtonString "##ImGuiFileDialogAddBookmark")) + { + if (!m_CurrentPath_Decomposition.empty()) + { + BookmarkStruct bookmark; + bookmark.name = m_CurrentPath_Decomposition.back(); + bookmark.path = m_CurrentPath; + m_Bookmarks.push_back(bookmark); + } + } + if (selectedBookmarkForEdition >= 0 && + selectedBookmarkForEdition < (int)m_Bookmarks.size()) + { + ImGui::SameLine(); + if (IMGUI_BUTTON(removeBookmarkButtonString "##ImGuiFileDialogAddBookmark")) + { + m_Bookmarks.erase(m_Bookmarks.begin() + selectedBookmarkForEdition); + if (selectedBookmarkForEdition == (int)m_Bookmarks.size()) + selectedBookmarkForEdition--; + } + + if (selectedBookmarkForEdition >= 0 && + selectedBookmarkForEdition < (int)m_Bookmarks.size()) + { + ImGui::SameLine(); + + ImGui::PushItemWidth(vSize.x - ImGui::GetCursorPosX()); + if (ImGui::InputText("##ImGuiFileDialogBookmarkEdit", BookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER)) + { + m_Bookmarks[selectedBookmarkForEdition].name = std::string(BookmarkEditBuffer); + } + ImGui::PopItemWidth(); + } + } + + ImGui::Separator(); + + if (!m_Bookmarks.empty()) + { + m_BookmarkClipper.Begin((int)m_Bookmarks.size(), ImGui::GetTextLineHeightWithSpacing()); + while (m_BookmarkClipper.Step()) + { + for (int i = m_BookmarkClipper.DisplayStart; i < m_BookmarkClipper.DisplayEnd; i++) + { + if (i < 0) continue; + const BookmarkStruct& bookmark = m_Bookmarks[i]; + ImGui::PushID(i); + if (ImGui::Selectable(bookmark.name.c_str(), selectedBookmarkForEdition == i, + ImGuiSelectableFlags_AllowDoubleClick) | + (selectedBookmarkForEdition == -1 && + bookmark.path == m_CurrentPath)) // select if path is current + { + selectedBookmarkForEdition = i; + ResetBuffer(BookmarkEditBuffer); + AppendToBuffer(BookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER, bookmark.name); + + if (ImGui::IsMouseDoubleClicked(0)) // apply path + { + SetPath(bookmark.path); + } + } + ImGui::PopID(); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", bookmark.path.c_str()); + } + } + m_BookmarkClipper.End(); + } + + ImGui::EndChild(); + } + + std::string IGFD::FileDialog::SerializeBookmarks() + { + std::string res; + + size_t idx = 0; + for (auto& it : m_Bookmarks) + { + if (idx++ != 0) + res += "##"; // ## because reserved by imgui, so an input text cant have ## + res += it.name + "##" + it.path; + } + + return res; + } + + void IGFD::FileDialog::DeserializeBookmarks(const std::string& vBookmarks) + { + if (!vBookmarks.empty()) + { + m_Bookmarks.clear(); + auto arr = 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]; + m_Bookmarks.push_back(bookmark); + } + } + } + } +#endif // USE_BOOKMARK + + ////////////////////////////////////////////////////////////////////////////// + //// OVERWRITE DIALOG //////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + bool IGFD::FileDialog::Confirm_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 (!m_IsOk && vLastAction) + { + return true; + } + + // if IsOk == true && no check of overwrite => return true for confirm the dialog + if (m_IsOk && vLastAction && !(dlg_flags & ImGuiFileDialogFlags_ConfirmOverwrite)) + { + return true; + } + + // if IsOk == true && check of overwrite => return false and show confirm to overwrite dialog + if ((m_OkResultToConfirm || (m_IsOk && vLastAction)) && (dlg_flags & ImGuiFileDialogFlags_ConfirmOverwrite)) + { + if (m_IsOk) // catched only one time + { + if (!IsFileExist(GetFilePathName())) // not existing => quit dialog + { + return true; + } + else // existing => confirm dialog to open + { + m_IsOk = false; + m_OkResultToConfirm = true; + } + } + + std::string name = OverWriteDialogTitleString "##" + dlg_title + dlg_key + "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(m_DialogCenterPos - ImGui::GetWindowSize() * 0.5f); // next frame needed for GetWindowSize to work + + ImGui::Text("%s", OverWriteDialogMessageString); + + if (IMGUI_BUTTON(OverWriteDialogConfirmButtonString)) + { + m_OkResultToConfirm = false; + m_IsOk = true; + res = true; + ImGui::CloseCurrentPopup(); + } + + ImGui::SameLine(); + + if (IMGUI_BUTTON(OverWriteDialogCancelButtonString)) + { + m_OkResultToConfirm = false; + m_IsOk = false; + res = false; + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + return res; + } + + + return false; + } + + bool IGFD::FileDialog::IsFileExist(const std::string& vFile) + { + std::ifstream docFile(vFile, std::ios::in); + if (docFile.is_open()) + { + docFile.close(); + return true; + } + 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 = 0; + res.filePathName = 0; + return res; +} + +// destroy only the content of vSelection_Pair +IMGUIFILEDIALOG_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair* vSelection_Pair) +{ + if (vSelection_Pair) + { + if (vSelection_Pair->fileName) + delete[] vSelection_Pair->fileName; + if (vSelection_Pair->filePathName) + delete[] vSelection_Pair->filePathName; + } +} + +// Return an initialized IGFD_Selection +IMGUIFILEDIALOG_API IGFD_Selection IGFD_Selection_Get(void) +{ + return { 0, 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->OpenDialog( + 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 (auto s : sel) + { + IGFD_Selection_Pair* pair = res.table + idx++; + + // fileName + 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 = 0; + + 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 = 0; + + 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 = 0; + + 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 = 0; + + 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_SetExtentionInfos(ImGuiFileDialog* vContext, + const char* vFilter, ImVec4 vColor, const char* vIcon) +{ + if (vContext) + { + vContext->SetExtentionInfos(vFilter, vColor, vIcon); + } +} + +IMGUIFILEDIALOG_API void IGFD_SetExtentionInfos2(ImGuiFileDialog* vContext, + const char* vFilter, float vR, float vG, float vB, float vA, const char* vIcon) +{ + if (vContext) + { + vContext->SetExtentionInfos(vFilter, ImVec4(vR, vG, vB, vA), vIcon); + } +} + +IMGUIFILEDIALOG_API bool IGFD_GetExtentionInfos(ImGuiFileDialog* vContext, + const char* vFilter, ImVec4* vOutColor, char** vOutIcon) +{ + if (vContext) + { + std::string icon; + bool res = vContext->GetExtentionInfos(vFilter, vOutColor, &icon); + 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_ClearExtentionInfos(ImGuiFileDialog* vContext) +{ + if (vContext) + { + vContext->ClearExtentionInfos(); + } +} + +#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 = 0; + + 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 \ No newline at end of file diff --git a/verilator/sim/imgui/ImGuiFileDialog.h b/verilator/sim/imgui/ImGuiFileDialog.h new file mode 100644 index 0000000..69193ad --- /dev/null +++ b/verilator/sim/imgui/ImGuiFileDialog.h @@ -0,0 +1,1119 @@ +/* +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. +*/ + +/* +----------------------------------------------------------------------------------------------------------------- +----------------------------------------------------------------------------------------------------------------- + +github repo : https://github.com/aiekick/ImGuiFileDialog + +----------------------------------------------------------------------------------------------------------------- +## Description : +----------------------------------------------------------------------------------------------------------------- + +this File Dialog is build on top of DearImGui +(On windows, need te lib Dirent : https://github.com/tronkko/dirent, use the branch 1.23 for avoid any issues) +Complete readme here : https://github.com/aiekick/ImGuiFileDialog/blob/master/README.md) + +this filedialog was created principally for have custom pane with widgets accrdoing to file extention. +it was not possible with native filedialog + +An example of the File Dialog integrated within the ImGui Demo App + +----------------------------------------------------------------------------------------------------------------- +## Features : +----------------------------------------------------------------------------------------------------------------- + +- Separate system for call and display + - can be many func calls with different params for one display func by ex +- Can use custom pane via function binding + - this pane can block the validation of the dialog + - can also display different things according to current filter and User Datas +- Support of Filter Custom Coloring / Icons / text +- Multi Selection (ctrl/shift + click) : + - 0 => infinite + - 1 => one file (default) + - n => n files +- Compatible with MacOs, Linux, Win + - On Win version you can list Drives +- Support of Modal/Standard dialog type +- Support both Mode : File Chooser or Directory Chooser +- Support filter collection / Custom filter name +- Support files Exploring with keys : Up / Down / Enter (open dir) / Backspace (come back) +- Support files Exploring by input char (case insensitive) +- Support bookmark creation/edition/call for directory (can have custom name corresponding to a path) +- Support input path edition by right click on a path button +- Support of a 'Confirm to Overwrite" dialog if File Exist + + +----------------------------------------------------------------------------------------------------------------- +## NameSpace / SingleTon +----------------------------------------------------------------------------------------------------------------- + +Use the Namespace IGFD (for avoid conflict with variables, struct and class names) + +you can display only one dialog at a time, this class is a simgleton and must be called like that : +ImGuiFileDialog::Instance()->method_of_your_choice() + +----------------------------------------------------------------------------------------------------------------- +## Simple Dialog : +----------------------------------------------------------------------------------------------------------------- + +Example code : +void drawGui() +{ + // open Dialog Simple + if (ImGui::Button("Open File Dialog")) + ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose File", ".cpp,.h,.hpp", "."); + + // display + if (ImGuiFileDialog::Instance()->FileDialog("ChooseFileDlgKey")) + { + // action if OK + if (ImGuiFileDialog::Instance()->IsOk == true) + { + std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); + std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); + // action + } + // close + ImGuiFileDialog::Instance()->CloseDialog("ChooseFileDlgKey"); + } +} + +----------------------------------------------------------------------------------------------------------------- +## Directory Chooser : +----------------------------------------------------------------------------------------------------------------- + +For have only a directory chooser, you just need to specify a filter null : + +Example code : +ImGuiFileDialog::Instance()->OpenDialog("ChooseDirDlgKey", "Choose a Directory", 0, "."); + +In this mode you can select any directory with one click, and open directory with double click + +----------------------------------------------------------------------------------------------------------------- +## Dialog with Custom Pane : +----------------------------------------------------------------------------------------------------------------- + +Example code : +static bool canValidateDialog = false; +inline void InfosPane(std::string& vFilter, IGFD::UserDatas vUserDatas, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog +{ + ImGui::TextColored(ImVec4(0, 1, 1, 1), "Infos Pane"); + ImGui::Text("Selected Filter : %s", vFilter.c_str()); + if (vUserDatas) + ImGui::Text("UserDatas : %s", vUserDatas); + ImGui::Checkbox("if not checked you cant validate the dialog", &canValidateDialog); + if (vCantContinue) + *vCantContinue = canValidateDialog; +} + +void drawGui() +{ + // open Dialog with Pane + if (ImGui::Button("Open File Dialog with a custom pane")) + ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose File", ".cpp,.h,.hpp", + ".", "", std::bind(&InfosPane, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), 350, 1, IGFD::UserDatas("InfosPane")); + + // display and action if ok + if (ImGuiFileDialog::Instance()->FileDialog("ChooseFileDlgKey")) + { + if (ImGuiFileDialog::Instance()->IsOk == true) + { + std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); + std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); + std::string filter = ImGuiFileDialog::Instance()->GetCurrentFilter(); + // here convert from string because a string was passed as a userDatas, but it can be what you want + std::string userDatas; + if (ImGuiFileDialog::Instance()->GetUserDatas()) + userDatas = std::string((const char*)ImGuiFileDialog::Instance()->GetUserDatas()); + auto selection = ImGuiFileDialog::Instance()->GetSelection(); // multiselection + + // action + } + // close + ImGuiFileDialog::Instance()->CloseDialog("ChooseFileDlgKey"); + } +} + +----------------------------------------------------------------------------------------------------------------- +## Filter Infos +----------------------------------------------------------------------------------------------------------------- + +You can define color for a filter type +Example code : +ImGuiFileDialog::Instance()->SetExtentionInfos(".cpp", ImVec4(1,1,0, 0.9)); +ImGuiFileDialog::Instance()->SetExtentionInfos(".h", ImVec4(0,1,0, 0.9)); +ImGuiFileDialog::Instance()->SetExtentionInfos(".hpp", ImVec4(0,0,1, 0.9)); +ImGuiFileDialog::Instance()->SetExtentionInfos(".md", ImVec4(1,0,1, 0.9)); + + +![alt text](doc/color_filter.png) + +and also specific icons (with icon font files) or file type names : + +Example code : +// add an icon for png files +ImGuiFileDialog::Instance()->SetExtentionInfos(".png", ImVec4(0,1,1,0.9), ICON_IMFDLG_FILE_TYPE_PIC); +// add a text for gif files (the default value is [File] +ImGuiFileDialog::Instance()->SetExtentionInfos(".gif", ImVec4(0, 1, 0.5, 0.9), "[GIF]"); + + +![alt text](doc/filter_Icon.png) + +----------------------------------------------------------------------------------------------------------------- +## Filter Collections +----------------------------------------------------------------------------------------------------------------- + +you can define a custom filter name who correspond to a group of filter + +you must use this syntax : custom_name1{filter1,filter2,filter3},custom_name2{filter1,filter2},filter1 +when you will select custom_name1, the gorup of filter 1 to 3 will be applied +the reserved char are {}, you cant use them for define filter name. + +Example code : +const char *filters = "Source files (*.cpp *.h *.hpp){.cpp,.h,.hpp},Image files (*.png *.gif *.jpg *.jpeg){.png,.gif,.jpg,.jpeg},.md"; +ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", ICON_IMFDLG_FOLDER_OPEN " Choose a File", filters, "."); + +## Multi Selection + +You can define in OpenDialog/OpenModal call the count file you wan to select : +- 0 => infinite +- 1 => one file only (default) +- n => n files only + +See the define at the end of these funcs after path. + +Example code : +ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose File", ".*,.cpp,.h,.hpp", "."); +ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose 1 File", ".*,.cpp,.h,.hpp", ".", 1); +ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose 5 File", ".*,.cpp,.h,.hpp", ".", 5); +ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose many File", ".*,.cpp,.h,.hpp", ".", 0); +ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose File", ".png,.jpg", + ".", "", std::bind(&InfosPane, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), 350, 1, "SaveFile"); // 1 file + +----------------------------------------------------------------------------------------------------------------- +## File Dialog Constraints +----------------------------------------------------------------------------------------------------------------- + +you can define min/max size of the dialog when you display It + +by ex : + +* MaxSize is the full display size +* MinSize in the half display size. + +Example code : +ImVec2 maxSize = ImVec2((float)display_w, (float)display_h); +ImVec2 minSize = maxSize * 0.5f; +ImGuiFileDialog::Instance()->FileDialog("ChooseFileDlgKey", ImGuiWindowFlags_NoCollapse, minSize, maxSize); + +----------------------------------------------------------------------------------------------------------------- +## Detail View Mode +----------------------------------------------------------------------------------------------------------------- + +You can have tables display like that. + +- uncomment "#define USE_IMGUI_TABLES" in you custom config file (CustomImGuiFileDialogConfig.h in this example) + +----------------------------------------------------------------------------------------------------------------- +## Exploring by keys +----------------------------------------------------------------------------------------------------------------- + +you can activate this feature by uncomment : "#define USE_EXPLORATION_BY_KEYS" +in you custom config file (CustomImGuiFileDialogConfig.h in this example) + +you can also uncomment the next lines for define your keys : + +* IGFD_KEY_UP => Up key for explore to the top +* IGFD_KEY_DOWN => Down key for explore to the bottom +* IGFD_KEY_ENTER => Enter key for open directory +* IGFD_KEY_BACKSPACE => BackSpace for comming back to the last directory + +you can also explore a file list by use the current key char. + +as you see the current item is flashed (by default for 1 sec) +you can define the flashing life time by yourself with the function + +Example code : +ImGuiFileDialog::Instance()->SetFlashingAttenuationInSeconds(1.0f); + +----------------------------------------------------------------------------------------------------------------- +## Bookmarks +----------------------------------------------------------------------------------------------------------------- + +you can create/edit/call path bookmarks and load/save them in file + +you can activate it by uncomment : "#define USE_BOOKMARK" + +in you custom config file (CustomImGuiFileDialogConfig.h in this example) + +you can also uncomment the next lines for customize it : +Example code : +#define bookmarkPaneWith 150.0f => width of the bookmark pane +#define IMGUI_TOGGLE_BUTTON ToggleButton => customize the Toggled button (button stamp must be : (const char* label, bool *toggle) +#define bookmarksButtonString "Bookmark" => the text in the toggle button +#define bookmarksButtonHelpString "Bookmark" => the helper text when mouse over the button +#define addBookmarkButtonString "+" => the button for add a bookmark +#define removeBookmarkButtonString "-" => the button for remove the selected bookmark + + +* you can select each bookmark for edit the displayed name corresponding to a path +* you must double click on the label for apply the bookmark + +you can also serialize/deserialize bookmarks by ex for load/save from/to file : (check the app sample by ex) +Example code : +Load => ImGuiFileDialog::Instance()->DeserializeBookmarks(bookmarString); +Save => std::string bookmarkString = ImGuiFileDialog::Instance()->SerializeBookmarks(); + +----------------------------------------------------------------------------------------------------------------- +## Path Edition : +----------------------------------------------------------------------------------------------------------------- + +if you click right on one of any path button, you can input or modify the path pointed by this button. +then press the validate key (Enter by default with GLFW) for validate the new path +or press the escape key (Escape by default with GLFW) for quit the input path edition + +see in this gif doc/inputPathEdition.gif : +1) button edition with mouse button right and escape key for quit the edition +2) focus the input and press validation for set path + +----------------------------------------------------------------------------------------------------------------- +## Confirm to OverWrite Dialog : +----------------------------------------------------------------------------------------------------------------- + +If you want avoid OverWrite your files after confirmation, +you can show a Dialog for confirm or cancel the OverWrite operation. + +You just need to define the flag ImGuiFileDialogFlags_ConfirmOverwrite +in your call to OpenDialog/OpenModal + +By default this flag is not set, since there is no pre-defined way to +define if a dialog will be for Open or Save behavior. (and its wanted :) ) + +Example code For Standard Dialog : +Example code : +ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", + ICON_IGFD_SAVE " Choose a File", filters, + ".", "", 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite); + +Example code For Modal Dialog : +Example code : +ImGuiFileDialog::Instance()->OpenModal("ChooseFileDlgKey", + ICON_IGFD_SAVE " Choose a File", filters, + ".", "", 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite); + +This dialog will only verify the file in the file field. +So Not to be used with GetSelection() + +The Confirm dialog will be a forced Modal Dialog, not moveable, displayed +in the center of the current FileDialog. + +As usual you can customize the dialog, +in you custom config file (CustomImGuiFileDialogConfig.h in this example) + +you can uncomment the next lines for customize it : + +Example code : +#define OverWriteDialogTitleString "The file Already Exist !" +#define OverWriteDialogMessageString "Would you like to OverWrite it ?" +#define OverWriteDialogConfirmButtonString "Confirm" +#define OverWriteDialogCancelButtonString "Cancel" + +----------------------------------------------------------------------------------------------------------------- +## Flags : +----------------------------------------------------------------------------------------------------------------- + +flag must be specified in OpenDialog or OpenModal +* ImGuiFileDialogFlags_ConfirmOverwrite => show confirm to overwrite dialog +* ImGuiFileDialogFlags_DontShowHiddenFiles => dont show hidden file (file starting with a .) + +----------------------------------------------------------------------------------------------------------------- +## Open / Save dialog Behavior : +----------------------------------------------------------------------------------------------------------------- + +There is no way to distinguish the "open dialog" behavior than "save dialog" behavior. +So you msut adapt the return according to your need : + +if you want open file(s) or directory(s), you must use : GetSelection() method. you will obtain a std::map of the selection +if you want create a file, you must use : GetFilePathName()/GetCurrentFileName() + +the return method's and comments : + +Example code : +std::map GetSelection(); // Open File behavior : will return selection via a map +std::string GetFilePathName(); // Create File behavior : will always return the content of the field with current filter extention and current path +std::string GetCurrentFileName(); // Create File behavior : will always return the content of the field with current filter extention +std::string GetCurrentPath(); // will return current path +std::string GetCurrentFilter(); // get selected filter +UserDatas GetUserDatas(); // get user datas send with Open Dialog + +----------------------------------------------------------------------------------------------------------------- +## C API +----------------------------------------------------------------------------------------------------------------- + +A C API is available let you include ImGuiFileDialog in your C project. +btw, ImGuiFileDialog depend of ImGui and dirent (for windows) + +Sample code with cimgui : + +// create ImGuiFileDialog +ImGuiFileDialog *cfileDialog = IGFD_Create(); + +// open dialog +if (igButton("Open File", buttonSize)) +{ + IGFD_OpenDialog(cfiledialog, + "filedlg", // dialog key (make it possible to have different treatment reagrding the dialog key + "Open a File", // dialog title + "c files(*.c *.h){.c,.h}", // dialog filter syntax : simple => .h,.c,.pp, etc and collections : text1{filter0,filter1,filter2}, text2{filter0,filter1,filter2}, etc.. + ".", // base directory for files scan + "", // base filename + 0, // a fucntion for display a right pane if you want + 0.0f, // base width of the pane + 0, // count selection : 0 infinite, 1 one file (default), n (n files) + "User data !", // some user datas + ImGuiFileDialogFlags_ConfirmOverwrite); // ImGuiFileDialogFlags +} + +ImGuiIO* ioptr = igGetIO(); +ImVec2 maxSize; +maxSize.x = ioptr->DisplaySize.x * 0.8f; +maxSize.y = ioptr->DisplaySize.y * 0.8f; +ImVec2 minSize; +minSize.x = maxSize.x * 0.25f; +minSize.y = maxSize.y * 0.25f; + +// display dialog +if (IGFD_DisplayDialog(cfiledialog, "filedlg", ImGuiWindowFlags_NoCollapse, minSize, maxSize)) +{ + if (IGFD_IsOk(cfiledialog)) // result ok + { + char* cfilePathName = IGFD_GetFilePathName(cfiledialog); + printf("GetFilePathName : %s\n", cfilePathName); + char* cfilePath = IGFD_GetCurrentPath(cfiledialog); + printf("GetCurrentPath : %s\n", cfilePath); + char* cfilter = IGFD_GetCurrentFilter(cfiledialog); + printf("GetCurrentFilter : %s\n", cfilter); + // here convert from string because a string was passed as a userDatas, but it can be what you want + void* cdatas = IGFD_GetUserDatas(cfiledialog); + if (cdatas) + printf("GetUserDatas : %s\n", (const char*)cdatas); + struct IGFD_Selection csel = IGFD_GetSelection(cfiledialog); // multi selection + printf("Selection :\n"); + for (int i = 0; i < (int)csel.count; i++) + { + printf("(%i) FileName %s => path %s\n", i, csel.table[i].fileName, csel.table[i].filePathName); + } + // action + + // destroy + if (cfilePathName) free(cfilePathName); + if (cfilePath) free(cfilePath); + if (cfilter) free(cfilter); + + IGFD_Selection_DestroyContent(&csel); + } + IGFD_CloseDialog(cfiledialog); +} + +// destroy ImGuiFileDialog +IGFD_Destroy(cfiledialog); + +----------------------------------------------------------------------------------------------------------------- +## How to Integrate ImGuiFileDialog in your project +----------------------------------------------------------------------------------------------------------------- + +### ImGuiFileDialog require : + +* dirent v1.23 (https://github.com/tronkko/dirent/tree/v1.23) lib, only for windows. Successfully tested with version v1.23 only +* Dear ImGui (https://github.com/ocornut/imgui/tree/master) (with/without tables widgets) + +### Customize ImGuiFileDialog : + +You just need to write your own config file by override the file : ImGuiFileDialog/ImGuiFileDialogConfig.h +like i do here with CustomImGuiFileDialogConfig.h + +After that, for let ImGuiFileDialog your own custom file, +you must define the preprocessor directive CUSTOM_IMGUIFILEDIALOG_CONFIG with the path of you custom config file. +This path must be relative to the directory where you put ImGuiFileDialog module. + +----------------------------------------------------------------------------------------------------------------- +----------------------------------------------------------------------------------------------------------------- + +Thats all. + +You can check by example in this repo with the file CustomImGuiFileDialogConfig.h : +- this trick was used for have custom icon font instead of labels for buttons or messages titles +- you can also use your custom imgui button, the button call stamp must be same by the way :) + +The Custom Icon Font (in CustomFont.cpp and CustomFont.h) was made with ImGuiFontStudio (https://github.com/aiekick/ImGuiFontStudio) i wrote for that :) +ImGuiFontStudio is using also ImGuiFileDialog. + +----------------------------------------------------------------------------------------------------------------- +----------------------------------------------------------------------------------------------------------------- +*/ + +#ifndef IMGUIFILEDIALOG_H +#define IMGUIFILEDIALOG_H + +#define IMGUIFILEDIALOG_VERSION "v0.5.6" + +#ifndef CUSTOM_IMGUIFILEDIALOG_CONFIG +#include "ImGuiFileDialogConfig.h" +#else // CUSTOM_IMGUIFILEDIALOG_CONFIG +#include CUSTOM_IMGUIFILEDIALOG_CONFIG +#endif // CUSTOM_IMGUIFILEDIALOG_CONFIG + +typedef int ImGuiFileDialogFlags; // -> enum ImGuiFileDialogFlags_ +enum ImGuiFileDialogFlags_ +{ + ImGuiFileDialogFlags_None = 0, + ImGuiFileDialogFlags_ConfirmOverwrite = (1 << 0), // show confirm to overwrite dialog + ImGuiFileDialogFlags_DontShowHiddenFiles = (1 << 1), // dont show hidden file (file starting with a .) + ImGuiFileDialogFlags_DisableCreateDirectoryButton = (1 << 2), // disable the create directory button + ImGuiFileDialogFlags_HideColumnType = (1 << 3), // hide column file type + ImGuiFileDialogFlags_HideColumnSize = (1 << 4), // hide column file size + ImGuiFileDialogFlags_HideColumnDate = (1 << 5), // hide column file date + ImGuiFileDialogFlags_Default = ImGuiFileDialogFlags_None // for the moment we have no default options but its comming :) +}; + +#ifdef __cplusplus + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace IGFD +{ + #ifndef MAX_FILE_DIALOG_NAME_BUFFER + #define MAX_FILE_DIALOG_NAME_BUFFER 1024 + #endif // MAX_FILE_DIALOG_NAME_BUFFER + + #ifndef MAX_PATH_BUFFER_SIZE + #define MAX_PATH_BUFFER_SIZE 1024 + #endif // MAX_PATH_BUFFER_SIZE + + struct FileExtentionInfosStruct + { + ImVec4 color = ImVec4(0, 0, 0, 0); + std::string icon; + FileExtentionInfosStruct() : color(0, 0, 0, 0) { } + FileExtentionInfosStruct(const ImVec4& vColor, const std::string& vIcon = "") { color = vColor; icon = vIcon; } + }; + + typedef void* UserDatas; + typedef std::function PaneFun; + + class FileDialog + { + + /////////////////////////////////////////////////////////////////////////////////////// + /// PRIVATE STRUCTS / ENUMS /////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + + private: +#ifdef USE_BOOKMARK + struct BookmarkStruct + { + std::string name; + std::string path; + }; +#endif // USE_BOOKMARK + + enum class SortingFieldEnum + { + FIELD_NONE = 0, + FIELD_FILENAME, + FIELD_TYPE, + FIELD_SIZE, + FIELD_DATE + }; + + struct FileInfoStruct + { + char type = ' '; + std::string filePath; + std::string fileName; + std::string fileName_optimized; // optimized for search => insensitivecase + std::string ext; + size_t fileSize = 0; // for sorting operations + std::string formatedFileSize; + std::string fileModifDate; + }; + + struct FilterInfosStruct + { + std::string filter; + std::set collectionfilters; + void clear() {filter.clear(); collectionfilters.clear();} + bool empty() const { return filter.empty() && collectionfilters.empty(); } + bool filterExist(const std::string& vFilter) {return filter == vFilter || collectionfilters.find(vFilter) != collectionfilters.end(); } + }; + + /////////////////////////////////////////////////////////////////////////////////////// + /// PRIVATE VARIABLES ///////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + + private: + std::vector m_FileList; + std::vector m_FilteredFileList; + std::unordered_map m_FileExtentionInfos; + std::string m_CurrentPath; + std::vector m_CurrentPath_Decomposition; + std::set m_SelectedFileNames; + std::string m_Name; + bool m_ShowDialog = false; + bool m_ShowDrives = false; + std::string m_LastSelectedFileName; // for shift multi selectio + std::vector m_Filters; + FilterInfosStruct m_SelectedFilter; + bool m_InputPathActivated = false; // show input for path edition + ImGuiListClipper m_FileListClipper; + ImVec2 m_DialogCenterPos = ImVec2(0, 0); // center pos for display the confirm overwrite dialog + int m_LastImGuiFrameCount = 0; // to be sure than only one dialog displayed per frame + float m_FooterHeight = 0.0f; + bool m_DrivesClicked = false; // events + bool m_PathClicked = false;// events + bool m_CanWeContinue = true;// events + bool m_OkResultToConfirm = false; // to confim if ok for OverWrite + bool m_IsOk = false; + bool m_CreateDirectoryMode = false; // for create directory mode + std::string m_HeaderFileName; // detail view column file + std::string m_HeaderFileType; // detail view column type + std::string m_HeaderFileSize; // detail view column size + std::string m_HeaderFileDate; // detail view column date + time + bool m_SortingDirection[4] = { true, true, true, true }; // detail view // true => Descending, false => Ascending + SortingFieldEnum m_SortingField = SortingFieldEnum::FIELD_FILENAME; // detail view sorting column + bool m_WantToQuit = false; // set to true for start the quit process of the dialog, specific behavior for select a file by double click for the moment + + std::string dlg_key; + std::string dlg_title; + std::string dlg_filters{}; + std::string dlg_path; + std::string dlg_defaultFileName; + std::string dlg_defaultExt; + ImGuiFileDialogFlags dlg_flags = ImGuiFileDialogFlags_None; + UserDatas dlg_userDatas = nullptr; + PaneFun dlg_optionsPane = nullptr; + float dlg_optionsPaneWidth = 0.0f; + std::string searchTag; + size_t dlg_countSelectionMax = 1; // 0 for infinite + bool dlg_modal = false; + +#ifdef USE_EXPLORATION_BY_KEYS + size_t m_FlashedItem = 0; // flash when select by char + float m_FlashAlpha = 0.0f; // flash when select by char + float m_FlashAlphaAttenInSecs = 1.0f; // fps display dependant + size_t m_LocateFileByInputChar_lastFileIdx = 0; + ImWchar m_LocateFileByInputChar_lastChar = 0; + int m_LocateFileByInputChar_InputQueueCharactersSize = 0; + bool m_LocateFileByInputChar_lastFound = false; +#endif // USE_EXPLORATION_BY_KEYS +#ifdef USE_BOOKMARK + float m_BookmarkWidth = 200.0f; + ImGuiListClipper m_BookmarkClipper; + std::vector m_Bookmarks; + bool m_BookmarkPaneShown = false; +#endif // USE_BOOKMARK + + /////////////////////////////////////////////////////////////////////////////////////// + /// PUBLIC PARAMS ///////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + + public: + char InputPathBuffer[MAX_PATH_BUFFER_SIZE] = ""; + char FileNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; + char DirectoryNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; + char SearchBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; + char VariadicBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; + +#ifdef USE_BOOKMARK + char BookmarkEditBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = ""; +#endif // USE_BOOKMARK + bool m_AnyWindowsHovered = false; // not remember why haha :) todo : to check if we can remove + + /////////////////////////////////////////////////////////////////////////////////////// + /// PUBLIC METHODS///////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + + public: + static FileDialog* Instance() // Singleton for easier accces form anywhere but only one dialog at a time + { + static FileDialog _instance; + return &_instance; + } + + public: + FileDialog(); // ImGuiFileDialog Constructor. can be used for have many dialog at same tiem (not possible with singleton) + virtual ~FileDialog(); // ImGuiFileDialog Destructor + + // standard dialog + void OpenDialog( // open simple dialog (path and fileName can be specified) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vPath, // path + const std::string& vFileName, // defaut file name + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + + void OpenDialog( // open simple dialog (path and filename are obtained from filePathName) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + + // with pane + void OpenDialog( // open dialog with custom right pane (path and fileName can be specified) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vPath, // path + const std::string& vFileName, // defaut file name + const PaneFun& vSidePane, // side pane + const float& vSidePaneWidth = 250.0f, // side pane width + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + + void OpenDialog( // open dialog with custom right pane (path and filename are obtained from filePathName) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) + const PaneFun& vSidePane, // side pane + const float& vSidePaneWidth = 250.0f, // side pane width + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + + // modal dialog + void OpenModal( // open simple modal (path and fileName can be specified) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vPath, // path + const std::string& vFileName, // defaut file name + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + + void OpenModal( // open simple modal (path and fielname are obtained from filePathName) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + + // with pane + void OpenModal( // open modal with custom right pane (path and filename are obtained from filePathName) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vPath, // path + const std::string& vFileName, // defaut file name + const PaneFun& vSidePane, // side pane + const float& vSidePaneWidth = 250.0f, // side pane width + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + + void OpenModal( // open modal with custom right pane (path and fielname are obtained from filePathName) + const std::string& vKey, // key dialog + const std::string& vTitle, // title + const char* vFilters, // filters + const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) + const PaneFun& vSidePane, // side pane + const float& vSidePaneWidth = 250.0f, // side pane width + const int& vCountSelectionMax = 1, // count selection max + UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + + // Display / Close dialog form + bool Display( // Display the dialog. return true if a result was obtained (Ok or not) + const std::string& vKey, // key dialog to display (if not the same key as defined by OpenDialog/Modal => no opening) + ImGuiWindowFlags vFlags = ImGuiWindowFlags_NoCollapse, // ImGuiWindowFlags + ImVec2 vMinSize = ImVec2(0, 0), // mininmal size contraint for the ImGuiWindow + ImVec2 vMaxSize = ImVec2(FLT_MAX, FLT_MAX)); // maximal size contraint for the ImGuiWindow + void Close(); // close dialog + + // queries + bool WasOpenedThisFrame(const std::string& vKey); // say if the dialog key was already opened this frame + bool WasOpenedThisFrame(); // say if the dialog was already opened this frame + bool IsOpened(const std::string& vKey); // say if the key is opened + bool IsOpened(); // say if the dialog is opened somewhere + std::string GetOpenedKey(); // return the dialog key who is opened, return nothing if not opened + + // get result + bool IsOk(); // true => Dialog Closed with Ok result / false : Dialog closed with cancel result + std::map GetSelection(); // Open File behavior : will return selection via a map + std::string GetFilePathName(); // Save File behavior : will always return the content of the field with current filter extention and current path + std::string GetCurrentFileName(); // Save File behavior : will always return the content of the field with current filter extention + std::string GetCurrentPath(); // will return current path + std::string GetCurrentFilter(); // will return selected filter + UserDatas GetUserDatas(); // will return user datas send with Open Dialog/Modal + + // extentions displaying + void SetExtentionInfos( // SetExtention datas for have custom display of particular file type + const std::string& vFilter, // extention filter to tune + const FileExtentionInfosStruct& vInfos); // Filter Extention Struct who contain Color and Icon/Text for the display of the file with extention filter + void SetExtentionInfos( // SetExtention datas for have custom display of particular file type + const std::string& vFilter, // extention filter to tune + const ImVec4& vColor, // wanted color for the display of the file with extention filter + const std::string& vIcon = ""); // wanted text or icon of the file with extention filter + bool GetExtentionInfos( // GetExtention datas. return true is extention exist + const std::string& vFilter, // extention filter (same as used in SetExtentionInfos) + ImVec4 *vOutColor, // color to retrieve + std::string* vOutIcon = 0); // icon or text to retrieve + void ClearExtentionInfos(); // clear extentions setttings + +#ifdef USE_EXPLORATION_BY_KEYS + void SetFlashingAttenuationInSeconds( // set the flashing time of the line in file list when use exploration keys + float vAttenValue); // set the attenuation (from flashed to not flashed) in seconds +#endif // USE_EXPLORATION_BY_KEYS +#ifdef USE_BOOKMARK + std::string SerializeBookmarks(); // serialize bookmarks : return bookmark buffer to save in a file + void DeserializeBookmarks( // deserialize bookmarks : load bookmar buffer to load in the dialog (saved from previous use with SerializeBookmarks()) + const std::string& vBookmarks); // bookmark buffer to load +#endif // USE_BOOKMARK + + /////////////////////////////////////////////////////////////////////////////////////// + /// PROTECTED'S METHODS /////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////// + + protected: + // dialog parts + virtual void DrawHeader(); // draw header part of the dialog (bookmark btn, dir creation, path composer, search bar) + virtual void DrawContent(); // draw content part of the dialog (bookmark pane, file list, side pane) + virtual bool DrawFooter(); // draw footer part of the dialog (file field, fitler combobox, ok/cancel btn's) + + // widgets components + virtual void DrawDirectoryCreation(); // draw directory creation widget + virtual void DrawPathComposer(); // draw path composer widget + virtual void DrawSearchBar(); // draw search bar + virtual void DrawFileListView(ImVec2 vSize); // draw file list viexw + virtual void DrawSidePane(float vHeight); // draw side pane +#ifdef USE_BOOKMARK + virtual void DrawBookMark(); // draw bookmark button +#endif // USE_BOOKMARK + + // others + bool SelectableItem(int vidx, const FileInfoStruct& vInfos, bool vSelected, const char* vFmt, ...); // selectable item for table + void ResetEvents(); // reset events (path, drives, continue) + void SetDefaultFileName(const std::string& vFileName); // set default file name + bool SelectDirectory(const FileInfoStruct& vInfos); // enter directory + void SelectFileName(const FileInfoStruct& vInfos); // select filename + void RemoveFileNameInSelection(const std::string& vFileName); // selection : remove a file name + void AddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName); // selection : add a file name + void SetPath(const std::string& vPath); // set the path of the dialog, will launch the directory scan for populate the file listview + void CompleteFileInfos(FileInfoStruct *vFileInfoStruct); // set time and date infos of a file (detail view mode) + void SortFields(SortingFieldEnum vSortingField = SortingFieldEnum::FIELD_NONE, bool vCanChangeOrder = false); // will sort a column + void ScanDir(const std::string& vPath); // scan the directory for retrieve the file list + void SetCurrentDir(const std::string& vPath); // define current directory for scan + bool CreateDir(const std::string& vPath); // create a directory on the file system + std::string ComposeNewPath(std::vector::iterator vIter); // compose a path from the compose path widget + void GetDrives(); // list drives on windows platform + void ParseFilters(const char* vFilters); // parse filter syntax, detect and parse filter collection + void SetSelectedFilterWithExt(const std::string& vFilter); // select filter + static std::string OptimizeFilenameForSearchOperations(std::string vFileName); // easier the search by lower case all filenames + void ApplyFilteringOnFileList(); // filter the file list accroding to the searh tags + bool Confirm_Or_OpenOverWriteFileDialog_IfNeeded(bool vLastAction, ImGuiWindowFlags vFlags); // treatment of the result, start the confirm to overwrite dialog if needed (if defined with flag) + bool IsFileExist(const std::string& vFile); // is file exist + +#ifdef USE_EXPLORATION_BY_KEYS + // file localization by input chat // widget flashing + void LocateByInputKey(); // select a file line in listview according to char key + bool LocateItem_Loop(ImWchar vC); // restrat for start of list view if not found a corresponding file + void ExploreWithkeys(); // select file/directory line in listview accroding to up/down enter/backspace keys + static bool FlashableSelectable( // custom flashing selectable widgets, for flash the selected line in a short time + const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, + bool vFlashing = false, const ImVec2& size = ImVec2(0, 0)); + void StartFlashItem(size_t vIdx); // define than an item must be flashed + bool BeginFlashItem(size_t vIdx); // start the flashing of a line in lsit view + void EndFlashItem(); // end the fleshing accrdoin to var m_FlashAlphaAttenInSecs +#endif // USE_EXPLORATION_BY_KEYS + +#ifdef USE_BOOKMARK + void DrawBookmarkPane(ImVec2 vSize); // draw bookmark pane +#endif // USE_BOOKMARK + }; +} +typedef IGFD::UserDatas IGFDUserDatas; +typedef IGFD::PaneFun IGFDPaneFun; +typedef IGFD::FileDialog ImGuiFileDialog; +#else // __cplusplus + typedef struct ImGuiFileDialog ImGuiFileDialog; + typedef struct IGFD_Selection_Pair IGFD_Selection_Pair; + typedef struct IGFD_Selection IGFD_Selection; +#endif // __cplusplus + +// C Interface + +#include + +#if defined _WIN32 || defined __CYGWIN__ + #ifdef IMGUIFILEDIALOG_NO_EXPORT + #define API + #else // IMGUIFILEDIALOG_NO_EXPORT + #define API __declspec(dllexport) + #endif // IMGUIFILEDIALOG_NO_EXPORT +#else // defined _WIN32 || defined __CYGWIN__ + #ifdef __GNUC__ + #define API __attribute__((__visibility__("default"))) + #else // __GNUC__ + #define API + #endif // __GNUC__ +#endif // defined _WIN32 || defined __CYGWIN__ + +#ifdef __cplusplus + #define IMGUIFILEDIALOG_API extern "C" API +#else // __cplusplus + #define IMGUIFILEDIALOG_API +#endif // __cplusplus + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///// C API //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + struct IGFD_Selection_Pair + { + char* fileName; + char* filePathName; + }; + + IMGUIFILEDIALOG_API IGFD_Selection_Pair IGFD_Selection_Pair_Get(); // return an initialized IGFD_Selection_Pair + IMGUIFILEDIALOG_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair *vSelection_Pair); // destroy the content of a IGFD_Selection_Pair + + struct IGFD_Selection + { + IGFD_Selection_Pair* table; // 0 + size_t count; // 0U + }; + + IMGUIFILEDIALOG_API IGFD_Selection IGFD_Selection_Get(); // return an initialized IGFD_Selection + IMGUIFILEDIALOG_API void IGFD_Selection_DestroyContent(IGFD_Selection* vSelection); // destroy the content of a IGFD_Selection + + // constructor / destructor + IMGUIFILEDIALOG_API ImGuiFileDialog* IGFD_Create(void); // create the filedialog context + IMGUIFILEDIALOG_API void IGFD_Destroy(ImGuiFileDialog *vContext); // destroy the filedialog context + + typedef void (*IGFD_PaneFun)(const char*, void*, bool*); // callback fucntion for display the pane + + IMGUIFILEDIALOG_API void IGFD_OpenDialog( // open a standard dialog + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vPath, // path + const char* vFileName, // defaut file name + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags + + IMGUIFILEDIALOG_API void IGFD_OpenDialog2( // open a standard dialog + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vFilePathName, // defaut file path name (path and filename witl be extracted from it) + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags + + IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog( // open a standard dialog with pane + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vPath, // path + const char* vFileName, // defaut file name + const IGFD_PaneFun vSidePane, // side pane + const float vSidePaneWidth, // side pane base width + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags + + IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog2( // open a standard dialog with pane + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vFilePathName, // defaut file name (path and filename witl be extracted from it) + const IGFD_PaneFun vSidePane, // side pane + const float vSidePaneWidth, // side pane base width + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags + + IMGUIFILEDIALOG_API void IGFD_OpenModal( // open a modal dialog + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vPath, // path + const char* vFileName, // defaut file name + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags + + IMGUIFILEDIALOG_API void IGFD_OpenModal2( // open a modal dialog + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vFilePathName, // defaut file name (path and filename witl be extracted from it) + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags + + IMGUIFILEDIALOG_API void IGFD_OpenPaneModal( // open a modal dialog with pane + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vPath, // path + const char* vFileName, // defaut file name + const IGFD_PaneFun vSidePane, // side pane + const float vSidePaneWidth, // side pane base width + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags + + IMGUIFILEDIALOG_API void IGFD_OpenPaneModal2( // open a modal dialog with pane + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog + const char* vTitle, // title + const char* vFilters, // filters/filter collections. set it to null for directory mode + const char* vFilePathName, // defaut file name (path and filename witl be extracted from it) + const IGFD_PaneFun vSidePane, // side pane + const float vSidePaneWidth, // side pane base width + const int vCountSelectionMax, // count selection max + void* vUserDatas, // user datas (can be retrieved in pane) + ImGuiFileDialogFlags vFlags); // ImGuiFileDialogFlags + + IMGUIFILEDIALOG_API bool IGFD_DisplayDialog( // Display the dialog + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey, // key dialog to display (if not the same key as defined by OpenDialog/Modal => no opening) + ImGuiWindowFlags vFlags, // ImGuiWindowFlags + ImVec2 vMinSize, // mininmal size contraint for the ImGuiWindow + ImVec2 vMaxSize); // maximal size contraint for the ImGuiWindow + + IMGUIFILEDIALOG_API void IGFD_CloseDialog( // Close the dialog + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API bool IGFD_IsOk( // true => Dialog Closed with Ok result / false : Dialog closed with cancel result + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API bool IGFD_WasKeyOpenedThisFrame( // say if the dialog key was already opened this frame + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vKey); + + IMGUIFILEDIALOG_API bool IGFD_WasOpenedThisFrame( // say if the dialog was already opened this frame + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API bool IGFD_IsKeyOpened( // say if the dialog key is opened + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vCurrentOpenedKey); // the dialog key + + IMGUIFILEDIALOG_API bool IGFD_IsOpened( // say if the dialog is opened somewhere + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API IGFD_Selection IGFD_GetSelection( // Open File behavior : will return selection via a map + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API char* IGFD_GetFilePathName( // Save File behavior : will always return the content of the field with current filter extention and current path + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API char* IGFD_GetCurrentFileName( // Save File behavior : will always return the content of the field with current filter extention + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API char* IGFD_GetCurrentPath( // will return current path + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API char* IGFD_GetCurrentFilter( // will return selected filter + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API void* IGFD_GetUserDatas( // will return user datas send with Open Dialog/Modal + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API void IGFD_SetExtentionInfos( // SetExtention datas for have custom display of particular file type + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vFilter, // extention filter to tune + ImVec4 vColor, // wanted color for the display of the file with extention filter + const char* vIconText); // wanted text or icon of the file with extention filter (can be sued with font icon) + + IMGUIFILEDIALOG_API void IGFD_SetExtentionInfos2( // SetExtention datas for have custom display of particular file type + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vFilter, // extention filter to tune + float vR, float vG, float vB, float vA, // wanted color channels RGBA for the display of the file with extention filter + const char* vIconText); // wanted text or icon of the file with extention filter (can be sued with font icon) + + IMGUIFILEDIALOG_API bool IGFD_GetExtentionInfos( + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vFilter, // extention filter (same as used in SetExtentionInfos) + ImVec4* vOutColor, // color to retrieve + char** vOutIconText); // icon or text to retrieve + + IMGUIFILEDIALOG_API void IGFD_ClearExtentionInfos( // clear extentions setttings + ImGuiFileDialog* vContext); // ImGuiFileDialog context + +#ifdef USE_EXPLORATION_BY_KEYS + IMGUIFILEDIALOG_API void IGFD_SetFlashingAttenuationInSeconds( // set the flashing time of the line in file list when use exploration keys + ImGuiFileDialog* vContext, // ImGuiFileDialog context + float vAttenValue); // set the attenuation (from flashed to not flashed) in seconds +#endif + +#ifdef USE_BOOKMARK + IMGUIFILEDIALOG_API char* IGFD_SerializeBookmarks( // serialize bookmarks : return bookmark buffer to save in a file + ImGuiFileDialog* vContext); // ImGuiFileDialog context + + IMGUIFILEDIALOG_API void IGFD_DeserializeBookmarks( // deserialize bookmarks : load bookmar buffer to load in the dialog (saved from previous use with SerializeBookmarks()) + ImGuiFileDialog* vContext, // ImGuiFileDialog context + const char* vBookmarks); // bookmark buffer to load +#endif + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif // IMGUIFILEDIALOG_H diff --git a/verilator/sim/imgui/ImGuiFileDialogConfig.h b/verilator/sim/imgui/ImGuiFileDialogConfig.h new file mode 100644 index 0000000..7caaddd --- /dev/null +++ b/verilator/sim/imgui/ImGuiFileDialogConfig.h @@ -0,0 +1,70 @@ +#pragma once + +// uncomment and modify defines under for customize ImGuiFileDialog + +//#define MAX_FILE_DIALOG_NAME_BUFFER 1024 +//#define MAX_PATH_BUFFER_SIZE 1024 + +//#define USE_EXPLORATION_BY_KEYS +// this mapping by default is for GLFW but you can use another +//#include +// Up key for explore to the top +//#define IGFD_KEY_UP GLFW_KEY_UP +// Down key for explore to the bottom +//#define IGFD_KEY_DOWN GLFW_KEY_DOWN +// Enter key for open directory +//#define IGFD_KEY_ENTER GLFW_KEY_ENTER +// BackSpace for comming back to the last directory +//#define IGFD_KEY_BACKSPACE GLFW_KEY_BACKSPACE + +// widget +// filter combobox width +//#define FILTER_COMBO_WIDTH 120.0f +// button widget use for compose path +//#define IMGUI_PATH_BUTTON ImGui::Button +// standard button +//#define IMGUI_BUTTON ImGui::Button + +// locales string +//#define createDirButtonString "+" +//#define okButtonString " OK" +//#define cancelButtonString " Cancel" +//#define resetButtonString "R" +//#define drivesButtonString "Drives" +//#define searchString "Search" +//#define dirEntryString "[DIR] " +//#define linkEntryString "[LINK] " +//#define fileEntryString "[FILE] " +//#define fileNameString "File Name : " +//#define dirNameString "Directory Path :" +//#define buttonResetSearchString "Reset search" +//#define buttonDriveString "Drives" +//#define buttonResetPathString "Reset to current directory" +//#define buttonCreateDirString "Create Directory" +//#define OverWriteDialogTitleString "The file Already Exist !" +//#define OverWriteDialogMessageString "Would you like to OverWrite it ?" +//#define OverWriteDialogConfirmButtonString "Confirm" +//#define OverWriteDialogCancelButtonString "Cancel" + +// DateTimeFormat +// see strftime functionin for customize +// "%Y/%m/%d %H:%M" give 2021:01:22 11:47 +// "%Y/%m/%d %i:%M%p" give 2021:01:22 11:45PM +//#define DateTimeFormat "%Y/%m/%d %i:%M%p" + +// theses icons will appear in table headers +//#define USE_CUSTOM_SORTING_ICON +//#define tableHeaderAscendingIcon "A|" +//#define tableHeaderDescendingIcon "D|" +//#define tableHeaderFileNameString " File name" +//#define tableHeaderFileTypeString " Type" +//#define tableHeaderFileSizeString " Size" +//#define tableHeaderFileDateTimeString " Date" + +//#define USE_BOOKMARK +//#define bookmarkPaneWith 150.0f +//#define IMGUI_TOGGLE_BUTTON ToggleButton +//#define bookmarksButtonString "Bookmark" +//#define bookmarksButtonHelpString "Bookmark" +//#define addBookmarkButtonString "+" +//#define removeBookmarkButtonString "-" diff --git a/verilator/sim/imgui/dirent/ChangeLog b/verilator/sim/imgui/dirent/ChangeLog new file mode 100644 index 0000000..d79cea2 --- /dev/null +++ b/verilator/sim/imgui/dirent/ChangeLog @@ -0,0 +1,129 @@ +2018-05-08 Toni Rönkkö + + * Version 1.23.2: fixes bad scandir prototype. + +2017-08-27 Toni Rönkkö + + * Version 1.23: support readdir_r and scandir functions. + +2017-07-18 Toni Rönkkö + + * Created release branches v1.22 and v1.21 to Git. Published version + 1.22 at softagalleria.net. + +2016-09-11 Toni Rönkkö + + * Version 1.22: added support for CMake. Thanks to Paul Fultz II. + +2014-09-25 Toni Rönkkö + + * Version 1.21: compiles correctly under Open Watcom. Thanks to + Virgil Banowetz for a patch! + +2014-04-07 Toni Rönkkö + + * Version 1.20.1: the zip file from the previous version did not open + correctly with Microsoft's compressed folders. Thanks to Alexandre + for info! + +2014-03-17 Toni Ronkko + + * Version 1.20: dirent.h compiles correctly in 64-bit architecture. + Thanks to Aaron Simmons! + +2014-03-03 Toni Ronkko + + * Version 1.13.2: define DT_LNK for compatibility with Unix + programs. Thanks to Joel Bruick for suggestion! + +2013-01-27 Toni Ronkko + + * Version 1.13.1: patch from Edward Berner fixes set_errno() on + Windows NT 4.0. + + * Revised wcstombs() and mbstowcs() wrappers to make sure that they do + not write past their target string. + + * PATH_MAX from windows.h includes zero terminator so there is no + need to add one extra byte to variables and structures. + +2012-12-12 Toni Ronkko + + * Version 1.13: use the traditional 8+3 file naming scheme if a file + name cannot be represented in the default ANSI code page. Now + compiles again with MSVC 6.0. Thanks to Konstantin Khomoutov for + testing. + +2012-10-01 Toni Ronkko + + * Version 1.12.1: renamed wide-character DIR structure _wDIR to + _WDIR (with capital W) in order to maintain compatibility with MingW. + +2012-09-30 Toni Ronkko + + * Version 1.12: define PATH_MAX and NAME_MAX. Added wide-character + variants _wDIR, _wdirent, _wopendir(), _wreaddir(), _wclosedir() and + _wrewinddir(). Thanks to Edgar Buerkle and Jan Nijtmans for ideas + and code. + + * Now avoiding windows.h. This allows dirent.h to be integrated + more easily into programs using winsock. Thanks to Fernando + Azaldegui. + +2011-03-15 Toni Ronkko + + * Version 1.11: defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0. + +2010-08-11 Toni Ronkko + + * Version 1.10: added d_type and d_namlen fields to dirent structure. + The former is especially useful for determining whether directory + entry represents a file or a directory. For more information, see + http://www.delorie.com/gnu/docs/glibc/libc_270.html + + * Improved conformance to the standards. For example, errno is now + set properly on failure and assert() is never used. Thanks to Peter + Brockam for suggestions. + + * Fixed a bug in rewinddir(): when using relative directory names, + change of working directory no longer causes rewinddir() to fail. + +2009-12-15 John Cunningham + + * Version 1.9: added rewinddir member function + +2008-01-18 Toni Ronkko + + * Version 1.8: Using FindFirstFileA and WIN32_FIND_DATAA to avoid + converting string between multi-byte and unicode representations. + This makes the code simpler and also allows the code to be compiled + under MingW. Thanks to Azriel Fasten for the suggestion. + +2007-03-04 Toni Ronkko + + * Bug fix: due to the strncpy_s() function this file only compiled in + Visual Studio 2005. Using the new string functions only when the + compiler version allows. + +2006-11-02 Toni Ronkko + + * Major update: removed support for Watcom C, MS-DOS and Turbo C to + simplify the file, updated the code to compile cleanly on Visual + Studio 2005 with both unicode and multi-byte character strings, + removed rewinddir() as it had a bug. + +2006-08-20 Toni Ronkko + + * Removed all remarks about MSVC 1.0, which is antiqued now. + Simplified comments by removing SGML tags. + +2002-05-14 Toni Ronkko + + * Embedded the function definitions directly to the header so that no + source modules need to be included in the Visual Studio project. + Removed all the dependencies to other projects so that this header + file can be used independently. + +1998-05-28 Toni Ronkko + + * First version. diff --git a/verilator/sim/imgui/dirent/LICENSE b/verilator/sim/imgui/dirent/LICENSE new file mode 100644 index 0000000..af04360 --- /dev/null +++ b/verilator/sim/imgui/dirent/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 1998-2019 Toni Ronkko + +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. diff --git a/verilator/sim/imgui/dirent/README.md b/verilator/sim/imgui/dirent/README.md new file mode 100644 index 0000000..53cc9f7 --- /dev/null +++ b/verilator/sim/imgui/dirent/README.md @@ -0,0 +1,96 @@ +# Dirent +Dirent is a C/C++ programming interface that allows programmers to retrieve +information about files and directories under Linux/UNIX. This project +provides Linux compatible Dirent interface for Microsoft Windows. + + +# Installation + +Download the latest Dirent installation package from +[GitHub](https://github.com/tronkko/dirent/releases) and +unpack the installation file with 7-zip, for example. The installation +package contains dirent.h file as well as a few example programs and +tests. + + +## Install Dirent for All Programs + +To make dirent.h available for all C/C++ programs, simply copy the +``include/dirent.h`` file to the system include directory. System include +directory contains header files such as assert.h and windows.h. In Visual +Studio 2008, for example, the system include may be found at +``C:\Program Files\Microsoft Visual Studio 9.0\VC\include``. + +Everything you need is included in the single dirent.h file, and you can +start using Dirent immediately -- there is no need to add files to your +Visual Studio project. + + +## Embed Dirent into Your Own Project + +If you wish to distribute dirent.h alongside with your own source code, then +copy ``include/dirent.h`` file to a new sub-directory within your project and +add that directory to include path on Windows while omitting the directory +under Linux/UNIX. This allows your project to be compiled against native +dirent.h on Linux/UNIX while substituting the functionality on Microsoft +Windows. + + +## Examples + +The installation package contains four example programs: + +Program | Purpose +-------- | ----------------------------------------------------------------- +ls | List files in a directory, e.g. ls "c:\Program Files" +find | Find files in subdirectories, e.g. find "c:\Program Files\CMake" +updatedb | Build database of files in a drive, e.g. updatedb c:\ +locate | Locate a file from database, e.g. locate notepad.exe + +To build the example programs, first install [CMake](https://cmake.org/). +Then, with CMake installed, open command prompt and create a temporary +directory ``c:\temp\dirent`` for the build files as + +``` +c:\ +mkdir temp +mkdir temp\dirent +cd temp\dirent +``` + +Generate build files as + +``` +cmake d:\dirent +``` + +where ``d:\dirent`` is the root directory of the Dirent package (containing +this README.md and LICENSE file). + +Once CMake is finished, open Visual Studio, load the generated dirent.sln file +from the build directory and build the solution. Once the build completes, run +the example programs from the command prompt as + +``` +cd Debug +ls . +find . +updatedb c:\ +locate cmd.exe +``` + + +# Copying + +Dirent may be freely distributed under the MIT license. See the +[LICENSE](LICENSE) file for details. + + +# Alternatives to Dirent + +I ported Dirent to Microsoft Windows in 1998 when only a few alternatives +were available. However, the situation has changed since then and nowadays +both [Cygwin](http://www.cygwin.com) and [MingW](http://www.mingw.org) +allow you to compile a great number of UNIX programs in Microsoft Windows. +They both provide a full dirent API as well as many other UNIX APIs. MingW +can even be used for commercial applications! diff --git a/verilator/sim/imgui/dirent/dirent.h b/verilator/sim/imgui/dirent/dirent.h new file mode 100644 index 0000000..f7a46da --- /dev/null +++ b/verilator/sim/imgui/dirent/dirent.h @@ -0,0 +1,1160 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; + DWORD n; + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ diff --git a/verilator/sim/sim_bus.cpp b/verilator/sim/sim_bus.cpp index bc72ac7..84dbe02 100644 --- a/verilator/sim/sim_bus.cpp +++ b/verilator/sim/sim_bus.cpp @@ -84,8 +84,8 @@ void SimBus::BeforeEval() } } else { - ioctl_download = 0; - ioctl_wr = 0; + *ioctl_download = 0; + *ioctl_wr = 0; } } @@ -113,4 +113,4 @@ SimBus::SimBus(DebugConsole c) { SimBus::~SimBus() { -} \ No newline at end of file +} diff --git a/verilator/sim_main.cpp b/verilator/sim_main.cpp index 62934bd..e189b8a 100644 --- a/verilator/sim_main.cpp +++ b/verilator/sim_main.cpp @@ -18,6 +18,7 @@ #include "sim_clock.h" #include "../imgui/imgui_memory_editor.h" +#include "../imgui/ImGuiFileDialog.h" // Debug GUI // --------- @@ -104,7 +105,7 @@ int verilate() { } // Output pixels on rising edge of pixel clock - if (clk_sys.IsRising() && top->top__DOT__system__DOT__ce_pix) { + if (clk_sys.IsRising() && top->top__DOT__ce_pix ) { uint32_t colour = 0xFF000000 | top->VGA_B << 16 | top->VGA_G << 8 | top->VGA_R; video.Clock(top->VGA_HB, top->VGA_VB, top->VGA_HS, top->VGA_VS, colour); } @@ -216,6 +217,8 @@ int main(int argc, char** argv, char** env) { if (ImGui::Button("RESET")) { resetSim(); } ImGui::SameLine(); if (ImGui::Button("START")) { run_enable = 1; } ImGui::SameLine(); if (ImGui::Button("STOP")) { run_enable = 0; } ImGui::SameLine(); + if (ImGui::Button("LOAD")) + ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose File", ".bin", "."); ImGui::Checkbox("RUN", &run_enable); ImGui::SliderInt("Batch size", &batchSize, 1, 250000); @@ -255,6 +258,22 @@ int main(int argc, char** argv, char** env) { //mem_edit_3.DrawContents(top->top__DOT__system__DOT__colram__DOT__mem, 2048, 0); //ImGui::End(); + // File Dialog to load rom + if (ImGuiFileDialog::Instance()->Display("ChooseFileDlgKey")) + { + // action if OK + if (ImGuiFileDialog::Instance()->IsOk()) + { + std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); + std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); + // action + bus.QueueDownload(filePathName, 0); + } + + // close + ImGuiFileDialog::Instance()->Close(); + } + ImGui::Begin("CPU Registers"); ImGui::Spacing(); ImGui::Text("PC 0x%04X", top->top__DOT__system__DOT__T80x__DOT__i_tv80_core__DOT__PC); From 54344f4c0654048942df995957518537115a28a0 Mon Sep 17 00:00:00 2001 From: Alan Steremberg Date: Mon, 28 Jun 2021 15:04:37 -0700 Subject: [PATCH 2/4] add file dialog and fix ioctl_addr --- build.sh | 6 +++--- verilator/sim.sln | 6 ++---- verilator/sim.vcxproj | 1 + verilator/sim.vcxproj.filters | 5 ++++- verilator/sim/sim_bus.cpp | 2 ++ 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/build.sh b/build.sh index 638953c..e7cab8b 100755 --- a/build.sh +++ b/build.sh @@ -5,9 +5,9 @@ make all cd .. # Verilate HDL -#cd verilator -#./verilate.sh -#cd .. +cd verilator +./verilate.sh +cd .. # Hexify roms od -An -t x1 -v src/os.bin > verilator/rom.hex diff --git a/verilator/sim.sln b/verilator/sim.sln index f1a60d4..5d42ff2 100644 --- a/verilator/sim.sln +++ b/verilator/sim.sln @@ -15,12 +15,10 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {A2A39241-8334-40A2-8D63-53A6CAEAEA05}.Debug|x64.ActiveCfg = Debug|x64 {A2A39241-8334-40A2-8D63-53A6CAEAEA05}.Debug|x64.Build.0 = Debug|x64 - {A2A39241-8334-40A2-8D63-53A6CAEAEA05}.Debug|x86.ActiveCfg = Debug|Win32 - {A2A39241-8334-40A2-8D63-53A6CAEAEA05}.Debug|x86.Build.0 = Debug|Win32 + {A2A39241-8334-40A2-8D63-53A6CAEAEA05}.Debug|x86.ActiveCfg = Debug|x64 {A2A39241-8334-40A2-8D63-53A6CAEAEA05}.Release|x64.ActiveCfg = Release|x64 {A2A39241-8334-40A2-8D63-53A6CAEAEA05}.Release|x64.Build.0 = Release|x64 - {A2A39241-8334-40A2-8D63-53A6CAEAEA05}.Release|x86.ActiveCfg = Release|Win32 - {A2A39241-8334-40A2-8D63-53A6CAEAEA05}.Release|x86.Build.0 = Release|Win32 + {A2A39241-8334-40A2-8D63-53A6CAEAEA05}.Release|x86.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/verilator/sim.vcxproj b/verilator/sim.vcxproj index b81382a..71029a2 100644 --- a/verilator/sim.vcxproj +++ b/verilator/sim.vcxproj @@ -60,6 +60,7 @@ + diff --git a/verilator/sim.vcxproj.filters b/verilator/sim.vcxproj.filters index 61d3ede..422d3a1 100644 --- a/verilator/sim.vcxproj.filters +++ b/verilator/sim.vcxproj.filters @@ -22,7 +22,7 @@ Source Files - Source Files + Source Files Source Files @@ -69,6 +69,9 @@ Source Files + + Source Files + diff --git a/verilator/sim/sim_bus.cpp b/verilator/sim/sim_bus.cpp index 84dbe02..107b73d 100644 --- a/verilator/sim/sim_bus.cpp +++ b/verilator/sim/sim_bus.cpp @@ -86,6 +86,8 @@ void SimBus::BeforeEval() else { *ioctl_download = 0; *ioctl_wr = 0; + *ioctl_addr = 0; + ioctl_next_addr = -1; } } From bef8b601f2451b783d3b214ba1b6c2d2db8f35ab Mon Sep 17 00:00:00 2001 From: Alan Steremberg Date: Mon, 28 Jun 2021 15:05:35 -0700 Subject: [PATCH 3/4] fix spelling --- src/os.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os.c b/src/os.c index c12a3af..b8b7064 100644 --- a/src/os.c +++ b/src/os.c @@ -20,7 +20,7 @@ void page_inputs() char label[5]; for (unsigned char j = 0; j < 6; j++) { - sprintf(label, "AOY%d", j + 1); + sprintf(label, "JOY%d", j + 1); write_string(label, 0xFF - (j * 2), 2, 4 + j); sprintf(label, "PAD%d", j + 1); From 6f700ec6f1f67c279246a57e1c1d83913b817a47 Mon Sep 17 00:00:00 2001 From: Alan Steremberg Date: Mon, 28 Jun 2021 15:28:50 -0700 Subject: [PATCH 4/4] added download queue restart --- verilator/sim/sim_bus.cpp | 9 +++++++-- verilator/sim/sim_bus.h | 6 ++++++ verilator/sim_main.cpp | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/verilator/sim/sim_bus.cpp b/verilator/sim/sim_bus.cpp index 8b28b0c..d2a018b 100644 --- a/verilator/sim/sim_bus.cpp +++ b/verilator/sim/sim_bus.cpp @@ -33,7 +33,10 @@ void SimBus::QueueDownload(std::string file, int index) { SimBus_DownloadChunk chunk = SimBus_DownloadChunk(file, index); downloadQueue.push(chunk); } - +void SimBus::QueueDownload(std::string file, int index, bool restart) { + SimBus_DownloadChunk chunk = SimBus_DownloadChunk(file, index, restart); + downloadQueue.push(chunk); +} bool SimBus::HasQueue() { return downloadQueue.size() > 0; } @@ -50,7 +53,9 @@ void SimBus::BeforeEval() // If last index differs from this one then reset the addresses if (currentDownload.index != *ioctl_index) { ioctl_next_addr = -1; } - + // if we want to restart the ioctl_addr then reset it + // leave it the same if we want to be able to load two roms sequentially + if (currentDownload.restart) { ioctl_next_addr = -1; } // Set address and index *ioctl_addr = ioctl_next_addr; *ioctl_index = currentDownload.index; diff --git a/verilator/sim/sim_bus.h b/verilator/sim/sim_bus.h index 795b7c4..f850dca 100644 --- a/verilator/sim/sim_bus.h +++ b/verilator/sim/sim_bus.h @@ -14,6 +14,7 @@ struct SimBus_DownloadChunk { public: std::string file; int index; + bool restart; SimBus_DownloadChunk() { file = ""; @@ -21,6 +22,10 @@ public: } SimBus_DownloadChunk(std::string file, int index) { + SimBus_DownloadChunk(file, index, false); + } + SimBus_DownloadChunk(std::string file, int index, bool restart) { + this->restart = restart; this->file = std::string(file); this->index = index; } @@ -41,6 +46,7 @@ public: void BeforeEval(void); void AfterEval(void); void QueueDownload(std::string file, int index); + void QueueDownload(std::string file, int index, bool restart); bool HasQueue(); SimBus(DebugConsole c); diff --git a/verilator/sim_main.cpp b/verilator/sim_main.cpp index baf64e0..3a49181 100644 --- a/verilator/sim_main.cpp +++ b/verilator/sim_main.cpp @@ -271,7 +271,7 @@ int main(int argc, char** argv, char** env) { std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); // action - bus.QueueDownload(filePathName, 0); + bus.QueueDownload(filePathName, 0, true); } // close