You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
stmdspgui/source/imgui/ImGuiFileDialog.cpp

3516 lines
91 KiB
C++

// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
/*
MIT License
Copyright (c) 2019-2020 Stephane Cuillerdier (aka aiekick)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "ImGuiFileDialog.h"
#ifdef __cplusplus
#include "imgui.h"
#include <float.h>
#include <string.h> // stricmp / strcasecmp
#include <sstream>
#include <iomanip>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#if defined (__EMSCRIPTEN__) // EMSCRIPTEN
#include <emscripten.h>
#endif // EMSCRIPTEN
#if defined(__WIN32__) || defined(_WIN32)
#ifndef WIN32
#define WIN32
#endif // WIN32
#define stat _stat
#define stricmp _stricmp
#include <cctype>
#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 <sys/types.h>
#include <dirent.h>
#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 <cstdlib>
#include <algorithm>
#include <iostream>
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 <ctime> 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<std::wstring> wsplitStringToVector(const std::wstring& text, char delimiter, bool pushEmpty)
{
std::vector<std::wstring> arr;
if (!text.empty())
{
std::wstring::size_type start = 0;
std::wstring::size_type end = text.find(delimiter, start);
while (end != std::wstring::npos)
{
std::wstring token = text.substr(start, end - start);
if (!token.empty() || (token.empty() && pushEmpty)) //-V728
arr.push_back(token);
start = end + 1;
end = text.find(delimiter, start);
}
std::wstring token = text.substr(start);
if (!token.empty() || (token.empty() && pushEmpty)) //-V728
arr.push_back(token);
}
return arr;
}
#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<std::string> splitStringToVector(const std::string& text, char delimiter, bool pushEmpty)
{
std::vector<std::string> 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<std::string> GetDrivesList()
{
std::vector<std::string> res;
#ifdef WIN32
const DWORD mydrives = 2048;
char lpBuffer[2048];
#define mini(a,b) (((a) < (b)) ? (a) : (b))
const DWORD countChars = mini(GetLogicalDriveStringsA(mydrives, lpBuffer), 2047);
#undef mini
if (countChars > 0)
{
std::string var = std::string(lpBuffer, (size_t)countChars);
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 explicitly right-aligned sizes not visibly match other widgets.
const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0;
const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x;
const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;
if (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;
const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;
if (disabled_item)
{
ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
item_add = ItemAdd(bb, id);
g.CurrentItemFlags = backup_item_flags;
}
else
{
item_add = ItemAdd(bb, id);
}
if (span_all_columns)
{
window->ClipRect.Min.x = backup_clip_rect_min_x;
window->ClipRect.Max.x = backup_clip_rect_max_x;
}
if (!item_add)
return false;
const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
if (disabled_item && !disabled_global)
PushDisabled(true);
// FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,
// which would be advantageous since most selectable are not selected.
if (span_all_columns && window->DC.CurrentColumns)
PushColumnsBackground();
else if (span_all_columns && g.CurrentTable)
TablePushBackgroundChannel();
// We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
ImGuiButtonFlags button_flags = 0;
if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }
if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; }
if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; }
const bool was_selected = selected;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
// Auto-select when moved into
// - This will be more fully fleshed in the range-select branch
// - This is not exposed as it won't nicely work with some user side handling of shift/control
// - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons
// - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope())
// - (2) usage will fail with clipped items
// The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent)
if (g.NavJustMovedToId == id)
selected = pressed = true;
// Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
{
if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
{
SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos));
g.NavDisableHighlight = true;
}
}
if (pressed)
MarkItemEdited(id);
if (flags & ImGuiSelectableFlags_AllowItemOverlap)
SetItemAllowOverlap();
// In this branch, Selectable() cannot toggle the selection so this will never trigger.
if (selected != was_selected) //-V547
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();
RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb);
// Automatically close popups
if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.CurrentItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
CloseCurrentPopup();
if (disabled_item && !disabled_global)
PopDisabled();
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags);
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)
{
ImGuiIO 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<std::string, std::string> IGFD::FileDialog::GetSelection()
{
std::map<std::string, std::string> 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<std::string>::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<std::string, FilterInfosStruct>
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