summaryrefslogtreecommitdiff
path: root/imfiledialog
diff options
context:
space:
mode:
Diffstat (limited to 'imfiledialog')
-rw-r--r--imfiledialog/ImFileDialog.cpp1397
-rw-r--r--imfiledialog/ImFileDialog.h136
-rw-r--r--imfiledialog/LICENSE21
3 files changed, 1554 insertions, 0 deletions
diff --git a/imfiledialog/ImFileDialog.cpp b/imfiledialog/ImFileDialog.cpp
new file mode 100644
index 0000000..8cbc2c9
--- /dev/null
+++ b/imfiledialog/ImFileDialog.cpp
@@ -0,0 +1,1397 @@
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+#include "ImFileDialog.h"
+
+#include <fstream>
+#include <algorithm>
+#include <sys/stat.h>
+#define IMGUI_DEFINE_MATH_OPERATORS
+#include "../imgui/imgui.h"
+#include "../imgui/imgui_internal.h"
+
+#include "../stb_image.h"
+
+#ifdef _WIN32
+#define NOMINMAX
+#include <windows.h>
+#include <shellapi.h>
+#include <lmcons.h>
+#pragma comment(lib, "Shell32.lib")
+#else
+#include <unistd.h>
+#include <pwd.h>
+#endif
+
+#define ICON_SIZE ImGui::GetFont()->FontSize + 3
+#define GUI_ELEMENT_SIZE std::max(GImGui->FontSize + 10.f, 24.f)
+#define DEFAULT_ICON_SIZE 32
+#define PI 3.141592f
+
+namespace ifd {
+ static const char* GetDefaultFolderIcon();
+ static const char* GetDefaultFileIcon();
+
+ /* UI CONTROLS */
+ bool FolderNode(const char* label, ImTextureID icon, bool& clicked)
+ {
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+
+ clicked = false;
+
+ ImU32 id = window->GetID(label);
+ int opened = window->StateStorage.GetInt(id, 0);
+ ImVec2 pos = window->DC.CursorPos;
+ const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= pos.x && g.IO.MousePos.x < pos.x + g.FontSize);
+ if (ImGui::InvisibleButton(label, ImVec2(-FLT_MIN, g.FontSize + g.Style.FramePadding.y * 2)))
+ {
+ if (is_mouse_x_over_arrow) {
+ int* p_opened = window->StateStorage.GetIntRef(id, 0);
+ opened = *p_opened = !*p_opened;
+ } else {
+ clicked = true;
+ }
+ }
+ bool hovered = ImGui::IsItemHovered();
+ bool active = ImGui::IsItemActive();
+ bool doubleClick = ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left);
+ if (doubleClick && hovered) {
+ int* p_opened = window->StateStorage.GetIntRef(id, 0);
+ opened = *p_opened = !*p_opened;
+ clicked = false;
+ }
+ if (hovered || active)
+ window->DrawList->AddRectFilled(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[active ? ImGuiCol_HeaderActive : ImGuiCol_HeaderHovered]));
+
+ // Icon, text
+ float icon_posX = pos.x + g.FontSize + g.Style.FramePadding.y;
+ float text_posX = icon_posX + g.Style.FramePadding.y + ICON_SIZE;
+ ImGui::RenderArrow(window->DrawList, ImVec2(pos.x, pos.y+g.Style.FramePadding.y), ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[((hovered && is_mouse_x_over_arrow) || opened) ? ImGuiCol_Text : ImGuiCol_TextDisabled]), opened ? ImGuiDir_Down : ImGuiDir_Right);
+ window->DrawList->AddImage(icon, ImVec2(icon_posX, pos.y), ImVec2(icon_posX + ICON_SIZE, pos.y + ICON_SIZE));
+ ImGui::RenderText(ImVec2(text_posX, pos.y + g.Style.FramePadding.y), label);
+ if (opened)
+ ImGui::TreePush(label);
+ return opened != 0;
+ }
+ bool FileNode(const char* label, ImTextureID icon) {
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+
+ //ImU32 id = window->GetID(label);
+ ImVec2 pos = window->DC.CursorPos;
+ bool ret = ImGui::InvisibleButton(label, ImVec2(-FLT_MIN, g.FontSize + g.Style.FramePadding.y * 2));
+
+ bool hovered = ImGui::IsItemHovered();
+ bool active = ImGui::IsItemActive();
+ if (hovered || active)
+ window->DrawList->AddRectFilled(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[active ? ImGuiCol_HeaderActive : ImGuiCol_HeaderHovered]));
+
+ // Icon, text
+ window->DrawList->AddImage(icon, ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_SIZE, pos.y + ICON_SIZE));
+ ImGui::RenderText(ImVec2(pos.x + g.Style.FramePadding.y + ICON_SIZE, pos.y + g.Style.FramePadding.y), label);
+
+ return ret;
+ }
+ bool PathBox(const char* label, std::filesystem::path& path, char* pathBuffer, ImVec2 size_arg) {
+ ImGuiWindow* window = ImGui::GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ bool ret = false;
+ const ImGuiID id = window->GetID(label);
+ int* state = window->StateStorage.GetIntRef(id, 0);
+
+ ImGui::SameLine();
+
+ ImGuiContext& g = *GImGui;
+ const ImGuiStyle& style = g.Style;
+ ImVec2 pos = window->DC.CursorPos;
+ ImVec2 uiPos = ImGui::GetCursorPos();
+ ImVec2 size = ImGui::CalcItemSize(size_arg, 200, GUI_ELEMENT_SIZE);
+ const ImRect bb(pos, pos + size);
+
+ // buttons
+ if (!(*state & 0b001)) {
+ ImGui::PushClipRect(bb.Min, bb.Max, false);
+
+ // background
+ bool hovered = g.IO.MousePos.x >= bb.Min.x && g.IO.MousePos.x <= bb.Max.x &&
+ g.IO.MousePos.y >= bb.Min.y && g.IO.MousePos.y <= bb.Max.y;
+ bool clicked = hovered && ImGui::IsMouseReleased(ImGuiMouseButton_Left);
+ bool anyOtherHC = false; // are any other items hovered or clicked?
+ window->DrawList->AddRectFilled(pos, pos + size, ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[(*state & 0b10) ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg]));
+
+ // fetch the buttons (so that we can throw some away if needed)
+ std::vector<std::string> btnList;
+ float totalWidth = 0.0f;
+ for (auto comp : path) {
+ std::string section = comp.u8string();
+ if (section.size() == 1 && (section[0] == '\\' || section[0] == '/'))
+ continue;
+
+ totalWidth += ImGui::CalcTextSize(section.c_str()).x + style.FramePadding.x * 2.0f + GUI_ELEMENT_SIZE;
+ btnList.push_back(section);
+ }
+ totalWidth -= GUI_ELEMENT_SIZE;
+
+ // UI buttons
+ ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ImGui::GetStyle().ItemSpacing.y));
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
+ bool isFirstElement = true;
+ for (size_t i = 0; i < btnList.size(); i++) {
+ if (totalWidth > size.x - 30 && i != btnList.size() - 1) { // trim some buttons if there's not enough space
+ float elSize = ImGui::CalcTextSize(btnList[i].c_str()).x + style.FramePadding.x * 2.0f + GUI_ELEMENT_SIZE;
+ totalWidth -= elSize;
+ continue;
+ }
+
+ ImGui::PushID(static_cast<int>(i));
+ if (!isFirstElement) {
+ ImGui::ArrowButtonEx("##dir_dropdown", ImGuiDir_Right, ImVec2(GUI_ELEMENT_SIZE, GUI_ELEMENT_SIZE));
+ anyOtherHC |= ImGui::IsItemHovered() | ImGui::IsItemClicked();
+ ImGui::SameLine();
+ }
+ if (ImGui::Button(btnList[i].c_str(), ImVec2(0, GUI_ELEMENT_SIZE))) {
+#ifdef _WIN32
+ std::string newPath = "";
+#else
+ std::string newPath = "/";
+#endif
+ for (size_t j = 0; j <= i; j++) {
+ newPath += btnList[j];
+#ifdef _WIN32
+ if (j != i)
+ newPath += "\\";
+#else
+ if (j != i)
+ newPath += "/";
+#endif
+ }
+ path = std::filesystem::u8path(newPath);
+ ret = true;
+ }
+ anyOtherHC |= ImGui::IsItemHovered() | ImGui::IsItemClicked();
+ ImGui::SameLine();
+ ImGui::PopID();
+
+ isFirstElement = false;
+ }
+ ImGui::PopStyleVar(2);
+
+
+ // click state
+ if (!anyOtherHC && clicked) {
+ strcpy(pathBuffer, path.u8string().c_str());
+ *state |= 0b001;
+ *state &= 0b011; // remove SetKeyboardFocus flag
+ }
+ else
+ *state &= 0b110;
+
+ // hover state
+ if (!anyOtherHC && hovered && !clicked)
+ *state |= 0b010;
+ else
+ *state &= 0b101;
+
+ ImGui::PopClipRect();
+
+ // allocate space
+ ImGui::SetCursorPos(uiPos);
+ ImGui::ItemSize(size);
+ }
+ // input box
+ else {
+ bool skipActiveCheck = false;
+ if (!(*state & 0b100)) {
+ skipActiveCheck = true;
+ ImGui::SetKeyboardFocusHere();
+ if (!ImGui::IsMouseClicked(ImGuiMouseButton_Left))
+ *state |= 0b100;
+ }
+ if (ImGui::InputTextEx("##pathbox_input", "", pathBuffer, 1024, size_arg, ImGuiInputTextFlags_EnterReturnsTrue)) {
+ std::string tempStr(pathBuffer);
+ if (std::filesystem::exists(tempStr))
+ path = std::filesystem::u8path(tempStr);
+ ret = true;
+ }
+ if (!skipActiveCheck && !ImGui::IsItemActive())
+ *state &= 0b010;
+ }
+
+ return ret;
+ }
+
+ bool FileIcon(const char* label, bool isSelected, ImTextureID icon, ImVec2 size, bool hasPreview, int previewWidth, int previewHeight)
+ {
+ ImGuiStyle& style = ImGui::GetStyle();
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+
+ float windowSpace = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x;
+ ImVec2 pos = window->DC.CursorPos;
+ bool ret = false;
+
+ if (ImGui::InvisibleButton(label, size))
+ ret = true;
+
+ bool hovered = ImGui::IsItemHovered();
+ bool active = ImGui::IsItemActive();
+ bool doubleClick = ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left);
+ if (doubleClick && hovered)
+ ret = true;
+
+
+ float iconSize = size.y - g.FontSize * 2;
+ float iconPosX = pos.x + (size.x - iconSize) / 2.0f;
+ ImVec2 textSize = ImGui::CalcTextSize(label, 0, true, size.x);
+
+
+ if (hovered || active || isSelected)
+ window->DrawList->AddRectFilled(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[active ? ImGuiCol_HeaderActive : (isSelected ? ImGuiCol_Header : ImGuiCol_HeaderHovered)]));
+
+ if (hasPreview) {
+ ImVec2 availSize = ImVec2(size.x, iconSize);
+
+ float scale = std::min<float>(availSize.x / previewWidth, availSize.y / previewHeight);
+ availSize.x = previewWidth * scale;
+ availSize.y = previewHeight * scale;
+
+ float previewPosX = pos.x + (size.x - availSize.x) / 2.0f;
+ float previewPosY = pos.y + (iconSize - availSize.y) / 2.0f;
+
+ window->DrawList->AddImage(icon, ImVec2(previewPosX, previewPosY), ImVec2(previewPosX + availSize.x, previewPosY + availSize.y));
+ } else
+ window->DrawList->AddImage(icon, ImVec2(iconPosX, pos.y), ImVec2(iconPosX + iconSize, pos.y + iconSize));
+
+ window->DrawList->AddText(g.Font, g.FontSize, ImVec2(pos.x + (size.x-textSize.x) / 2.0f, pos.y + iconSize), ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_Text]), label, 0, size.x);
+
+
+ float lastButtomPos = ImGui::GetItemRectMax().x;
+ float thisButtonPos = lastButtomPos + style.ItemSpacing.x + size.x; // Expected position if next button was on same line
+ if (thisButtonPos < windowSpace)
+ ImGui::SameLine();
+
+ return ret;
+ }
+
+ FileDialog::FileData::FileData(const std::filesystem::path& path) {
+ std::error_code ec;
+ Path = path;
+ IsDirectory = std::filesystem::is_directory(path, ec);
+ Size = std::filesystem::file_size(path, ec);
+
+ struct stat attr;
+ stat(path.u8string().c_str(), &attr);
+ DateModified = attr.st_ctime;
+
+ HasIconPreview = false;
+ IconPreview = nullptr;
+ IconPreviewData = nullptr;
+ IconPreviewHeight = 0;
+ IconPreviewWidth = 0;
+ }
+
+ FileDialog::FileDialog() {
+ m_isOpen = false;
+ m_type = 0;
+ m_calledOpenPopup = false;
+ m_sortColumn = 0;
+ m_sortDirection = ImGuiSortDirection_Ascending;
+ m_filterSelection = 0;
+ m_inputTextbox[0] = 0;
+ m_pathBuffer[0] = 0;
+ m_searchBuffer[0] = 0;
+ m_newEntryBuffer[0] = 0;
+ m_selectedFileItem = -1;
+ m_zoom = 1.0f;
+
+ m_previewLoader = nullptr;
+ m_previewLoaderRunning = false;
+
+ m_setDirectory(std::filesystem::current_path(), false);
+
+ // favorites are available on every OS
+ FileTreeNode* quickAccess = new FileTreeNode("Quick Access");
+ quickAccess->Read = true;
+ m_treeCache.push_back(quickAccess);
+
+#ifdef _WIN32
+ wchar_t username[UNLEN + 1] = { 0 };
+ DWORD username_len = UNLEN + 1;
+ GetUserNameW(username, &username_len);
+
+ std::wstring userPath = L"C:\\Users\\" + std::wstring(username) + L"\\";
+
+ // Quick Access / Bookmarks
+ quickAccess->Children.push_back(new FileTreeNode(userPath + L"Desktop"));
+ quickAccess->Children.push_back(new FileTreeNode(userPath + L"Documents"));
+ quickAccess->Children.push_back(new FileTreeNode(userPath + L"Downloads"));
+ quickAccess->Children.push_back(new FileTreeNode(userPath + L"Pictures"));
+
+ // OneDrive
+ FileTreeNode* oneDrive = new FileTreeNode(userPath + L"OneDrive");
+ m_treeCache.push_back(oneDrive);
+
+ // This PC
+ FileTreeNode* thisPC = new FileTreeNode("This PC");
+ thisPC->Read = true;
+ if (std::filesystem::exists(userPath + L"3D Objects"))
+ thisPC->Children.push_back(new FileTreeNode(userPath + L"3D Objects"));
+ thisPC->Children.push_back(new FileTreeNode(userPath + L"Desktop"));
+ thisPC->Children.push_back(new FileTreeNode(userPath + L"Documents"));
+ thisPC->Children.push_back(new FileTreeNode(userPath + L"Downloads"));
+ thisPC->Children.push_back(new FileTreeNode(userPath + L"Music"));
+ thisPC->Children.push_back(new FileTreeNode(userPath + L"Pictures"));
+ thisPC->Children.push_back(new FileTreeNode(userPath + L"Videos"));
+ DWORD d = GetLogicalDrives();
+ for (int i = 0; i < 26; i++)
+ if (d & (1 << i))
+ thisPC->Children.push_back(new FileTreeNode(std::string(1, 'A' + i) + ":"));
+ m_treeCache.push_back(thisPC);
+#else
+ std::error_code ec;
+
+ // Quick Access
+ struct passwd *pw;
+ uid_t uid;
+ uid = geteuid();
+ pw = getpwuid(uid);
+ if (pw) {
+ std::string homePath = "/home/" + std::string(pw->pw_name);
+
+ if (std::filesystem::exists(homePath, ec))
+ quickAccess->Children.push_back(new FileTreeNode(homePath));
+ if (std::filesystem::exists(homePath + "/Desktop", ec))
+ quickAccess->Children.push_back(new FileTreeNode(homePath + "/Desktop"));
+ if (std::filesystem::exists(homePath + "/Documents", ec))
+ quickAccess->Children.push_back(new FileTreeNode(homePath + "/Documents"));
+ if (std::filesystem::exists(homePath + "/Downloads", ec))
+ quickAccess->Children.push_back(new FileTreeNode(homePath + "/Downloads"));
+ if (std::filesystem::exists(homePath + "/Pictures", ec))
+ quickAccess->Children.push_back(new FileTreeNode(homePath + "/Pictures"));
+ }
+
+ // This PC
+ FileTreeNode* thisPC = new FileTreeNode("This PC");
+ thisPC->Read = true;
+ for (const auto& entry : std::filesystem::directory_iterator("/", ec)) {
+ if (std::filesystem::is_directory(entry, ec))
+ thisPC->Children.push_back(new FileTreeNode(entry.path().u8string()));
+ }
+ m_treeCache.push_back(thisPC);
+#endif
+ }
+ FileDialog::~FileDialog() {
+ m_clearIconPreview();
+ m_clearIcons();
+
+ for (auto fn : m_treeCache)
+ m_clearTree(fn);
+ m_treeCache.clear();
+ }
+ bool FileDialog::Save(const std::string& key, const std::string& title, const std::string& filter, const std::string& startingDir)
+ {
+ if (!m_currentKey.empty())
+ return false;
+
+ m_currentKey = key;
+ m_currentTitle = title + "###" + key;
+ m_isOpen = true;
+ m_calledOpenPopup = false;
+ m_result.clear();
+ m_inputTextbox[0] = 0;
+ m_selections.clear();
+ m_selectedFileItem = -1;
+ m_isMultiselect = false;
+ m_type = IFD_DIALOG_SAVE;
+
+ m_parseFilter(filter);
+ if (!startingDir.empty())
+ m_setDirectory(std::filesystem::u8path(startingDir), false);
+ else
+ m_setDirectory(m_currentDirectory, false); // refresh contents
+
+ return true;
+ }
+ bool FileDialog::Open(const std::string& key, const std::string& title, const std::string& filter, bool isMultiselect, const std::string& startingDir)
+ {
+ if (!m_currentKey.empty())
+ return false;
+
+ m_currentKey = key;
+ m_currentTitle = title + "###" + key;
+ m_isOpen = true;
+ m_calledOpenPopup = false;
+ m_result.clear();
+ m_inputTextbox[0] = 0;
+ m_selections.clear();
+ m_selectedFileItem = -1;
+ m_isMultiselect = isMultiselect;
+ m_type = filter.empty() ? IFD_DIALOG_DIRECTORY : IFD_DIALOG_FILE;
+
+ m_parseFilter(filter);
+ if (!startingDir.empty())
+ m_setDirectory(std::filesystem::u8path(startingDir), false);
+ else
+ m_setDirectory(m_currentDirectory, false); // refresh contents
+
+ return true;
+ }
+ bool FileDialog::IsDone(const std::string& key, int window_w, int window_h)
+ {
+ bool isMe = m_currentKey == key;
+
+ if (isMe && m_isOpen) {
+ ImGui::SetNextWindowPos({(window_w/2.0f)-400, (window_h/2.0f)-300});
+ ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_Always);
+ if (!m_calledOpenPopup) {
+ ImGui::OpenPopup(m_currentTitle.c_str());
+ m_calledOpenPopup = true;
+ }
+
+ if (ImGui::BeginPopupModal(m_currentTitle.c_str(), &m_isOpen, ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove)) {
+ m_renderFileDialog();
+ ImGui::EndPopup();
+ }
+ else m_isOpen = false;
+ }
+
+ return isMe && !m_isOpen;
+ }
+ void FileDialog::Close()
+ {
+ m_currentKey.clear();
+ m_backHistory = std::stack<std::filesystem::path>();
+ m_forwardHistory = std::stack<std::filesystem::path>();
+
+ // clear the tree
+ for (auto fn : m_treeCache) {
+ for (auto item : fn->Children) {
+ for (auto ch : item->Children)
+ m_clearTree(ch);
+ item->Children.clear();
+ item->Read = false;
+ }
+ }
+
+ // free icon textures
+ m_clearIconPreview();
+ m_clearIcons();
+ }
+
+ void FileDialog::m_select(const std::filesystem::path& path, bool isCtrlDown)
+ {
+ bool multiselect = isCtrlDown && m_isMultiselect;
+
+ if (!multiselect) {
+ m_selections.clear();
+ m_selections.push_back(path);
+ } else {
+ auto it = std::find(m_selections.begin(), m_selections.end(), path);
+ if (it != m_selections.end())
+ m_selections.erase(it);
+ else
+ m_selections.push_back(path);
+ }
+
+ if (m_selections.size() == 1) {
+ std::string filename = m_selections[0].filename().u8string();
+ if (filename.size() == 0)
+ filename = m_selections[0].u8string(); // drive
+
+ strcpy(m_inputTextbox, filename.c_str());
+ }
+ else {
+ std::string textboxVal = "";
+ for (const auto& sel : m_selections) {
+ std::string filename = sel.filename().u8string();
+ if (filename.size() == 0)
+ filename = sel.u8string();
+
+ textboxVal += "\"" + filename + "\", ";
+ }
+ strcpy(m_inputTextbox, textboxVal.substr(0, textboxVal.size() - 2).c_str());
+ }
+ }
+
+ bool FileDialog::m_finalize(const std::string& filename)
+ {
+ bool hasResult = (!filename.empty() && m_type != IFD_DIALOG_DIRECTORY) || m_type == IFD_DIALOG_DIRECTORY;
+
+ if (hasResult) {
+ if (!m_isMultiselect || m_selections.size() <= 1) {
+ std::filesystem::path path = std::filesystem::u8path(filename);
+ if (path.is_absolute()) m_result.push_back(path);
+ else m_result.push_back(m_currentDirectory / path);
+ if (m_type == IFD_DIALOG_DIRECTORY || m_type == IFD_DIALOG_FILE) {
+ if (!std::filesystem::exists(m_result.back())) {
+ m_result.clear();
+ return false;
+ }
+ }
+ }
+ else {
+ for (const auto& sel : m_selections) {
+ if (sel.is_absolute()) m_result.push_back(sel);
+ else m_result.push_back(m_currentDirectory / sel);
+ if (m_type == IFD_DIALOG_DIRECTORY || m_type == IFD_DIALOG_FILE) {
+ if (!std::filesystem::exists(m_result.back())) {
+ m_result.clear();
+ return false;
+ }
+ }
+ }
+ }
+
+ if (m_type == IFD_DIALOG_SAVE) {
+ // add the extension
+ if (m_filterSelection < m_filterExtensions.size() && m_filterExtensions[m_filterSelection].size() > 0) {
+ if (!m_result.back().has_extension()) {
+ std::string extAdd = m_filterExtensions[m_filterSelection][0];
+ m_result.back().replace_extension(extAdd);
+ }
+ }
+ }
+ }
+
+ m_isOpen = false;
+
+ return true;
+ }
+ void FileDialog::m_parseFilter(const std::string& filter)
+ {
+ m_filter = "";
+ m_filterExtensions.clear();
+ m_filterSelection = 0;
+
+ if (filter.empty())
+ return;
+
+ std::vector<std::string> exts;
+
+ size_t lastSplit = 0, lastExt = 0;
+ bool inExtList = false;
+ for (size_t i = 0; i < filter.size(); i++) {
+ if (filter[i] == ',') {
+ if (!inExtList)
+ lastSplit = i + 1;
+ else {
+ exts.push_back(filter.substr(lastExt, i - lastExt));
+ lastExt = i + 1;
+ }
+ }
+ else if (filter[i] == '{') {
+ std::string filterName = filter.substr(lastSplit, i - lastSplit);
+ if (filterName == ".*") {
+ m_filter += std::string(std::string("All Files (*.*)\0").c_str(), 16);
+ m_filterExtensions.push_back(std::vector<std::string>());
+ }
+ else
+ m_filter += std::string((filterName + "\0").c_str(), filterName.size() + 1);
+ inExtList = true;
+ lastExt = i + 1;
+ }
+ else if (filter[i] == '}') {
+ exts.push_back(filter.substr(lastExt, i - lastExt));
+ m_filterExtensions.push_back(exts);
+ exts.clear();
+
+ inExtList = false;
+ }
+ }
+ if (lastSplit != 0) {
+ std::string filterName = filter.substr(lastSplit);
+ if (filterName == ".*") {
+ m_filter += std::string(std::string("All Files (*.*)\0").c_str(), 16);
+ m_filterExtensions.push_back(std::vector<std::string>());
+ }
+ else
+ m_filter += std::string((filterName + "\0").c_str(), filterName.size() + 1);
+ }
+ }
+
+ void* FileDialog::m_getIcon(const std::filesystem::path& path)
+ {
+#ifdef _WIN32
+ if (m_icons.count(path.u8string()) > 0)
+ return m_icons[path.u8string()];
+
+ std::string pathU8 = path.u8string();
+
+ std::error_code ec;
+ m_icons[pathU8] = nullptr;
+
+ DWORD attrs = 0;
+ UINT flags = SHGFI_ICON | SHGFI_LARGEICON;
+ if (!std::filesystem::exists(path, ec)) {
+ flags |= SHGFI_USEFILEATTRIBUTES;
+ attrs = FILE_ATTRIBUTE_DIRECTORY;
+ }
+
+ SHFILEINFOW fileInfo = { 0 };
+ std::wstring pathW = path.wstring();
+ for (int i = 0; i < pathW.size(); i++)
+ if (pathW[i] == '/')
+ pathW[i] = '\\';
+ SHGetFileInfoW(pathW.c_str(), attrs, &fileInfo, sizeof(SHFILEINFOW), flags);
+
+ if (fileInfo.hIcon == nullptr)
+ return nullptr;
+
+ // check if icon is already loaded
+ auto itr = std::find(m_iconIndices.begin(), m_iconIndices.end(), fileInfo.iIcon);
+ if (itr != m_iconIndices.end()) {
+ const std::string& existingIconFilepath = m_iconFilepaths[itr - m_iconIndices.begin()];
+ m_icons[pathU8] = m_icons[existingIconFilepath];
+ return m_icons[pathU8];
+ }
+
+ m_iconIndices.push_back(fileInfo.iIcon);
+ m_iconFilepaths.push_back(pathU8);
+
+ ICONINFO iconInfo = { 0 };
+ GetIconInfo(fileInfo.hIcon, &iconInfo);
+
+ if (iconInfo.hbmColor == nullptr)
+ return nullptr;
+
+ DIBSECTION ds;
+ GetObject(iconInfo.hbmColor, sizeof(ds), &ds);
+ int byteSize = ds.dsBm.bmWidth * ds.dsBm.bmHeight * (ds.dsBm.bmBitsPixel / 8);
+
+ if (byteSize == 0)
+ return nullptr;
+
+ uint8_t* data = (uint8_t*)malloc(byteSize);
+ GetBitmapBits(iconInfo.hbmColor, byteSize, data);
+
+ m_icons[pathU8] = this->CreateTexture(data, ds.dsBm.bmWidth, ds.dsBm.bmHeight, 0);
+
+ free(data);
+
+ return m_icons[pathU8];
+#else
+ if (m_icons.count(path.u8string()) > 0)
+ return m_icons[path.u8string()];
+
+ std::string pathU8 = path.u8string();
+
+ m_icons[pathU8] = nullptr;
+
+ std::error_code ec;
+ int iconID = 1;
+ if (std::filesystem::is_directory(path, ec))
+ iconID = 0;
+
+ // check if icon is already loaded
+ auto itr = std::find(m_iconIndices.begin(), m_iconIndices.end(), iconID);
+ if (itr != m_iconIndices.end()) {
+ const std::string& existingIconFilepath = m_iconFilepaths[itr - m_iconIndices.begin()];
+ m_icons[pathU8] = m_icons[existingIconFilepath];
+ return m_icons[pathU8];
+ }
+
+ m_iconIndices.push_back(iconID);
+ m_iconFilepaths.push_back(pathU8);
+
+ ImVec4 wndBg = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
+
+ // light theme - load default icons
+ if ((wndBg.x + wndBg.y + wndBg.z) / 3.0f > 0.5f) {
+ uint8_t* data = (uint8_t*)ifd::GetDefaultFileIcon();
+ if (iconID == 0)
+ data = (uint8_t*)ifd::GetDefaultFolderIcon();
+ m_icons[pathU8] = this->CreateTexture(data, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, 0);
+ }
+ // dark theme - invert the colors
+ else {
+ uint8_t* data = (uint8_t*)ifd::GetDefaultFileIcon();
+ if (iconID == 0)
+ data = (uint8_t*)ifd::GetDefaultFolderIcon();
+
+ uint8_t* invData = (uint8_t*)malloc(DEFAULT_ICON_SIZE * DEFAULT_ICON_SIZE * 4);
+ for (int y = 0; y < 32; y++) {
+ for (int x = 0; x < 32; x++) {
+ int index = (y* DEFAULT_ICON_SIZE + x) * 4;
+ invData[index + 0] = 255 - data[index + 0];
+ invData[index + 1] = 255 - data[index + 1];
+ invData[index + 2] = 255 - data[index + 2];
+ invData[index + 3] = data[index + 3];
+ }
+ }
+ m_icons[pathU8] = this->CreateTexture(invData, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, 0);
+
+ free(invData);
+ }
+
+ return m_icons[pathU8];
+#endif
+ }
+ void FileDialog::m_clearIcons()
+ {
+ std::vector<unsigned int> deletedIcons;
+
+ // delete textures
+ for (auto& icon : m_icons) {
+ unsigned int ptr = (unsigned int)((uintptr_t)icon.second);
+ if (std::count(deletedIcons.begin(), deletedIcons.end(), ptr)) // skip duplicates
+ continue;
+
+ deletedIcons.push_back(ptr);
+ //DeleteTexture(icon.second);
+ }
+ m_iconFilepaths.clear();
+ m_iconIndices.clear();
+ m_icons.clear();
+ }
+ void FileDialog::m_refreshIconPreview()
+ {
+ if (m_zoom >= 5.0f) {
+ if (m_previewLoader == nullptr) {
+ m_previewLoaderRunning = true;
+ m_previewLoader = new std::thread(&FileDialog::m_loadPreview, this);
+ }
+ } else
+ m_clearIconPreview();
+ }
+ void FileDialog::m_clearIconPreview()
+ {
+ m_stopPreviewLoader();
+
+ for (auto& data : m_content) {
+ if (!data.HasIconPreview)
+ continue;
+
+ data.HasIconPreview = false;
+ this->DeleteTexture(data.IconPreview);
+
+ if (data.IconPreviewData != nullptr) {
+ stbi_image_free(data.IconPreviewData);
+ data.IconPreviewData = nullptr;
+ }
+ }
+ }
+ void FileDialog::m_stopPreviewLoader()
+ {
+ if (m_previewLoader != nullptr) {
+ m_previewLoaderRunning = false;
+
+ if (m_previewLoader && m_previewLoader->joinable())
+ m_previewLoader->join();
+
+ delete m_previewLoader;
+ m_previewLoader = nullptr;
+ }
+ }
+ void FileDialog::m_loadPreview()
+ {
+ for (size_t i = 0; m_previewLoaderRunning && i < m_content.size(); i++) {
+ auto& data = m_content[i];
+
+ if (data.HasIconPreview)
+ continue;
+
+ if (data.Path.has_extension()) {
+ std::string ext = data.Path.extension().u8string();
+ if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".bmp" || ext == ".tga") {
+ int width, height, nrChannels;
+ unsigned char* image = stbi_load(data.Path.u8string().c_str(), &width, &height, &nrChannels, STBI_rgb_alpha);
+
+ if (image == nullptr || width == 0 || height == 0)
+ continue;
+
+ data.HasIconPreview = true;
+ data.IconPreviewData = image;
+ data.IconPreviewWidth = width;
+ data.IconPreviewHeight = height;
+ }
+ }
+ }
+
+ m_previewLoaderRunning = false;
+ }
+ void FileDialog::m_clearTree(FileTreeNode* node)
+ {
+ if (node == nullptr)
+ return;
+
+ for (auto n : node->Children)
+ m_clearTree(n);
+
+ delete node;
+ node = nullptr;
+ }
+ void FileDialog::m_setDirectory(const std::filesystem::path& p, bool addHistory)
+ {
+ bool isSameDir = m_currentDirectory == p;
+
+ if (addHistory && !isSameDir)
+ m_backHistory.push(m_currentDirectory);
+
+ m_currentDirectory = p;
+#ifdef _WIN32
+ // drives don't work well without the backslash symbol
+ if (p.u8string().size() == 2 && p.u8string()[1] == ':')
+ m_currentDirectory = std::filesystem::u8path(p.u8string() + "\\");
+#endif
+
+ m_clearIconPreview();
+ m_content.clear(); // p == "" after this line, due to reference
+ m_selectedFileItem = -1;
+
+ if (m_type == IFD_DIALOG_DIRECTORY || m_type == IFD_DIALOG_FILE)
+ m_inputTextbox[0] = 0;
+ m_selections.clear();
+
+ if (!isSameDir) {
+ m_searchBuffer[0] = 0;
+ m_clearIcons();
+ }
+
+ if (p.u8string() == "Quick Access") {
+ for (auto& node : m_treeCache) {
+ if (node->Path == p)
+ for (auto& c : node->Children)
+ m_content.push_back(FileData(c->Path));
+ }
+ }
+ else if (p.u8string() == "This PC") {
+ for (auto& node : m_treeCache) {
+ if (node->Path == p)
+ for (auto& c : node->Children)
+ m_content.push_back(FileData(c->Path));
+ }
+ }
+ else {
+ std::error_code ec;
+ if (std::filesystem::exists(m_currentDirectory, ec))
+ for (const auto& entry : std::filesystem::directory_iterator(m_currentDirectory, ec)) {
+ FileData info(entry.path());
+
+ // skip files when IFD_DIALOG_DIRECTORY
+ if (!info.IsDirectory && m_type == IFD_DIALOG_DIRECTORY)
+ continue;
+
+ // check if filename matches search query
+ if (m_searchBuffer[0]) {
+ std::string filename = info.Path.u8string();
+
+ std::string filenameSearch = filename;
+ std::string query(m_searchBuffer);
+ std::transform(filenameSearch.begin(), filenameSearch.end(), filenameSearch.begin(), ::tolower);
+ std::transform(query.begin(), query.end(), query.begin(), ::tolower);
+
+ if (filenameSearch.find(query, 0) == std::string::npos)
+ continue;
+ }
+
+ // check if extension matches
+ if (!info.IsDirectory && m_type != IFD_DIALOG_DIRECTORY) {
+ if (m_filterSelection < m_filterExtensions.size()) {
+ const auto& exts = m_filterExtensions[m_filterSelection];
+ if (exts.size() > 0) {
+ std::string extension = info.Path.extension().u8string();
+
+ // extension not found? skip
+ if (std::count(exts.begin(), exts.end(), extension) == 0)
+ continue;
+ }
+ }
+ }
+
+ m_content.push_back(info);
+ }
+ }
+
+ m_sortContent(m_sortColumn, m_sortDirection);
+ m_refreshIconPreview();
+ }
+ void FileDialog::m_sortContent(unsigned int column, unsigned int sortDirection)
+ {
+ // 0 -> name, 1 -> date, 2 -> size
+ m_sortColumn = column;
+ m_sortDirection = sortDirection;
+
+ // split into directories and files
+ std::partition(m_content.begin(), m_content.end(), [](const FileData& data) {
+ return data.IsDirectory;
+ });
+
+ if (m_content.size() > 0) {
+ // find where the file list starts
+ size_t fileIndex = 0;
+ for (; fileIndex < m_content.size(); fileIndex++)
+ if (!m_content[fileIndex].IsDirectory)
+ break;
+
+ // compare function
+ auto compareFn = [column, sortDirection](const FileData& left, const FileData& right) -> bool {
+ // name
+ if (column == 0) {
+ std::string lName = left.Path.u8string();
+ std::string rName = right.Path.u8string();
+
+ std::transform(lName.begin(), lName.end(), lName.begin(), ::tolower);
+ std::transform(rName.begin(), rName.end(), rName.begin(), ::tolower);
+
+ int comp = lName.compare(rName);
+
+ if (sortDirection == ImGuiSortDirection_Ascending)
+ return comp < 0;
+ return comp > 0;
+ }
+ // date
+ else if (column == 1) {
+ if (sortDirection == ImGuiSortDirection_Ascending)
+ return left.DateModified < right.DateModified;
+ else
+ return left.DateModified > right.DateModified;
+ }
+ // size
+ else if (column == 2) {
+ if (sortDirection == ImGuiSortDirection_Ascending)
+ return left.Size < right.Size;
+ else
+ return left.Size > right.Size;
+ }
+
+ return false;
+ };
+
+ // sort the directories
+ std::sort(m_content.begin(), m_content.begin() + fileIndex, compareFn);
+
+ // sort the files
+ std::sort(m_content.begin() + fileIndex, m_content.end(), compareFn);
+ }
+ }
+
+ void FileDialog::m_renderTree(FileTreeNode* node)
+ {
+ // directory
+ std::error_code ec;
+ ImGui::PushID(node);
+ bool isClicked = false;
+ std::string displayName = node->Path.stem().u8string();
+ if (displayName.size() == 0)
+ displayName = node->Path.u8string();
+ if (FolderNode(displayName.c_str(), (ImTextureID)m_getIcon(node->Path), isClicked)) {
+ if (!node->Read) {
+ // cache children if it's not already cached
+ if (std::filesystem::exists(node->Path, ec))
+ for (const auto& entry : std::filesystem::directory_iterator(node->Path, ec)) {
+ if (std::filesystem::is_directory(entry, ec))
+ node->Children.push_back(new FileTreeNode(entry.path().u8string()));
+ }
+ node->Read = true;
+ }
+
+ // display children
+ for (auto c : node->Children)
+ m_renderTree(c);
+
+ ImGui::TreePop();
+ }
+ if (isClicked)
+ m_setDirectory(node->Path);
+ ImGui::PopID();
+ }
+ void FileDialog::m_renderContent()
+ {
+ if (ImGui::IsMouseClicked(ImGuiMouseButton_Right))
+ m_selectedFileItem = -1;
+
+ // table view
+ if (m_zoom == 1.0f) {
+ if (ImGui::BeginTable("##contentTable", 3, /*ImGuiTableFlags_Resizable |*/ ImGuiTableFlags_Sortable, ImVec2(0, -FLT_MIN))) {
+ // header
+ ImGui::TableSetupColumn("Name##filename", ImGuiTableColumnFlags_WidthStretch, 0.0f -1.0f, 0);
+ ImGui::TableSetupColumn("Date modified##filedate", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, 0.0f, 1);
+ ImGui::TableSetupColumn("Size##filesize", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, 0.0f, 2);
+ ImGui::TableSetupScrollFreeze(0, 1);
+ ImGui::TableHeadersRow();
+
+ // sort
+ if (ImGuiTableSortSpecs* sortSpecs = ImGui::TableGetSortSpecs()) {
+ if (sortSpecs->SpecsDirty) {
+ sortSpecs->SpecsDirty = false;
+ m_sortContent(sortSpecs->Specs->ColumnUserID, sortSpecs->Specs->SortDirection);
+ }
+ }
+
+ // content
+ int fileId = 0;
+ for (auto& entry : m_content) {
+ std::string filename = entry.Path.filename().u8string();
+ if (filename.size() == 0)
+ filename = entry.Path.u8string(); // drive
+
+ bool isSelected = std::count(m_selections.begin(), m_selections.end(), entry.Path);
+
+ ImGui::TableNextRow();
+
+ // file name
+ ImGui::TableSetColumnIndex(0);
+ ImGui::Image((ImTextureID)m_getIcon(entry.Path), ImVec2(ICON_SIZE, ICON_SIZE));
+ ImGui::SameLine();
+ if (ImGui::Selectable(filename.c_str(), isSelected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) {
+ std::error_code ec;
+ bool isDir = std::filesystem::is_directory(entry.Path, ec);
+
+ if (ImGui::IsMouseDoubleClicked(0)) {
+ if (isDir) {
+ m_setDirectory(entry.Path);
+ break;
+ } else
+ m_finalize(filename);
+ } else {
+ if ((isDir && m_type == IFD_DIALOG_DIRECTORY) || !isDir)
+ m_select(entry.Path, ImGui::GetIO().KeyCtrl);
+ }
+ }
+ if (ImGui::IsItemClicked(ImGuiMouseButton_Right))
+ m_selectedFileItem = fileId;
+ fileId++;
+
+ // date
+ ImGui::TableSetColumnIndex(1);
+ auto tm = std::localtime(&entry.DateModified);
+ if (tm != nullptr)
+ ImGui::Text("%d/%d/%d %02d:%02d", tm->tm_mon + 1, tm->tm_mday, 1900 + tm->tm_year, tm->tm_hour, tm->tm_min);
+ else ImGui::Text("---");
+
+ // size
+ ImGui::TableSetColumnIndex(2);
+ ImGui::Text("%.3f KiB", entry.Size/1024.0f);
+ }
+
+ ImGui::EndTable();
+ }
+ }
+ // "icon" view
+ else {
+ // content
+ int fileId = 0;
+ for (auto& entry : m_content) {
+ if (entry.HasIconPreview && entry.IconPreviewData != nullptr) {
+ entry.IconPreview = this->CreateTexture(entry.IconPreviewData, entry.IconPreviewWidth, entry.IconPreviewHeight, 1);
+ stbi_image_free(entry.IconPreviewData);
+ entry.IconPreviewData = nullptr;
+ }
+
+ std::string filename = entry.Path.filename().u8string();
+ if (filename.size() == 0)
+ filename = entry.Path.u8string(); // drive
+
+ bool isSelected = std::count(m_selections.begin(), m_selections.end(), entry.Path);
+
+ if (FileIcon(filename.c_str(), isSelected, entry.HasIconPreview ? entry.IconPreview : (ImTextureID)m_getIcon(entry.Path), ImVec2(32 + 16 * m_zoom, 32 + 16 * m_zoom), entry.HasIconPreview, entry.IconPreviewWidth, entry.IconPreviewHeight)) {
+ std::error_code ec;
+ bool isDir = std::filesystem::is_directory(entry.Path, ec);
+
+ if (ImGui::IsMouseDoubleClicked(0)) {
+ if (isDir) {
+ m_setDirectory(entry.Path);
+ break;
+ }
+ else
+ m_finalize(filename);
+ }
+ else {
+ if ((isDir && m_type == IFD_DIALOG_DIRECTORY) || !isDir)
+ m_select(entry.Path, ImGui::GetIO().KeyCtrl);
+ }
+ }
+ if (ImGui::IsItemClicked(ImGuiMouseButton_Right))
+ m_selectedFileItem = fileId;
+ fileId++;
+ }
+ }
+ }
+ void FileDialog::m_renderPopups()
+ {
+ bool openAreYouSureDlg = false, openNewFileDlg = false, openNewDirectoryDlg = false;
+ if (ImGui::BeginPopupContextItem("##dir_context")) {
+ if (ImGui::Selectable("New file"))
+ openNewFileDlg = true;
+ if (ImGui::Selectable("New directory"))
+ openNewDirectoryDlg = true;
+ if (m_selectedFileItem != -1 && ImGui::Selectable("Delete"))
+ openAreYouSureDlg = true;
+ ImGui::EndPopup();
+ }
+ if (openAreYouSureDlg)
+ ImGui::OpenPopup("Are you sure?##delete");
+ if (openNewFileDlg)
+ ImGui::OpenPopup("Enter file name##newfile");
+ if (openNewDirectoryDlg)
+ ImGui::OpenPopup("Enter directory name##newdir");
+ if (ImGui::BeginPopupModal("Are you sure?##delete")) {
+ if (m_selectedFileItem >= static_cast<int>(m_content.size()) || m_content.size() == 0)
+ ImGui::CloseCurrentPopup();
+ else {
+ const FileData& data = m_content[m_selectedFileItem];
+ ImGui::TextWrapped("Are you sure you want to delete %s?", data.Path.filename().u8string().c_str());
+ if (ImGui::Button("Yes")) {
+ std::error_code ec;
+ std::filesystem::remove_all(data.Path, ec);
+ m_setDirectory(m_currentDirectory, false); // refresh
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("No"))
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndPopup();
+ }
+ if (ImGui::BeginPopupModal("Enter file name##newfile")) {
+ ImGui::PushItemWidth(250.0f);
+ ImGui::InputText("##newfilename", m_newEntryBuffer, 1024); // TODO: remove hardcoded literals
+ ImGui::PopItemWidth();
+
+ if (ImGui::Button("OK")) {
+ std::ofstream out((m_currentDirectory / std::string(m_newEntryBuffer)).string());
+ out << "";
+ out.close();
+
+ m_setDirectory(m_currentDirectory, false); // refresh
+ m_newEntryBuffer[0] = 0;
+
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Cancel")) {
+ m_newEntryBuffer[0] = 0;
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::EndPopup();
+ }
+ if (ImGui::BeginPopupModal("Enter directory name##newdir")) {
+ ImGui::PushItemWidth(250.0f);
+ ImGui::InputText("##newfilename", m_newEntryBuffer, 1024); // TODO: remove hardcoded literals
+ ImGui::PopItemWidth();
+
+ if (ImGui::Button("OK")) {
+ std::error_code ec;
+ std::filesystem::create_directory(m_currentDirectory / std::string(m_newEntryBuffer), ec);
+ m_setDirectory(m_currentDirectory, false); // refresh
+ m_newEntryBuffer[0] = 0;
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Cancel")) {
+ ImGui::CloseCurrentPopup();
+ m_newEntryBuffer[0] = 0;
+ }
+ ImGui::EndPopup();
+ }
+ }
+ void FileDialog::m_renderFileDialog()
+ {
+ /***** TOP BAR *****/
+ bool noBackHistory = m_backHistory.empty(), noForwardHistory = m_forwardHistory.empty();
+
+ ImGui::PushStyleColor(ImGuiCol_Button, 0);
+ if (noBackHistory) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
+ if (ImGui::ArrowButtonEx("##back", ImGuiDir_Left, ImVec2(GUI_ELEMENT_SIZE, GUI_ELEMENT_SIZE), m_backHistory.empty() * ImGuiItemFlags_Disabled)) {
+ std::filesystem::path newPath = m_backHistory.top();
+ m_backHistory.pop();
+ m_forwardHistory.push(m_currentDirectory);
+
+ m_setDirectory(newPath, false);
+ }
+ if (noBackHistory) ImGui::PopStyleVar();
+ ImGui::SameLine();
+
+ if (noForwardHistory) ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
+ if (ImGui::ArrowButtonEx("##forward", ImGuiDir_Right, ImVec2(GUI_ELEMENT_SIZE, GUI_ELEMENT_SIZE), m_forwardHistory.empty() * ImGuiItemFlags_Disabled)) {
+ std::filesystem::path newPath = m_forwardHistory.top();
+ m_forwardHistory.pop();
+ m_backHistory.push(m_currentDirectory);
+
+ m_setDirectory(newPath, false);
+ }
+ if (noForwardHistory) ImGui::PopStyleVar();
+ ImGui::SameLine();
+
+ if (ImGui::ArrowButtonEx("##up", ImGuiDir_Up, ImVec2(GUI_ELEMENT_SIZE, GUI_ELEMENT_SIZE))) {
+ if (m_currentDirectory.has_parent_path())
+ m_setDirectory(m_currentDirectory.parent_path());
+ }
+
+ std::filesystem::path curDirCopy = m_currentDirectory;
+ if (PathBox("##pathbox", curDirCopy, m_pathBuffer, ImVec2(-250, GUI_ELEMENT_SIZE)))
+ m_setDirectory(curDirCopy);
+ ImGui::SameLine();
+
+ ImGui::SameLine();
+ ImGui::PopStyleColor();
+
+ if (ImGui::InputTextEx("##searchTB", "Search", m_searchBuffer, 128, ImVec2(-FLT_MIN, GUI_ELEMENT_SIZE), 0)) // TODO: no hardcoded literals
+ m_setDirectory(m_currentDirectory, false); // refresh
+
+
+
+ /***** CONTENT *****/
+ float bottomBarHeight = (GImGui->FontSize + ImGui::GetStyle().FramePadding.y + ImGui::GetStyle().ItemSpacing.y * 2.0f) * 2;
+ if (ImGui::BeginTable("##table", 2, ImGuiTableFlags_Resizable, ImVec2(0, -bottomBarHeight))) {
+ ImGui::TableSetupColumn("##tree", ImGuiTableColumnFlags_WidthFixed, 125.0f);
+ ImGui::TableSetupColumn("##content", ImGuiTableColumnFlags_WidthStretch);
+ ImGui::TableNextRow();
+
+ // the tree on the left side
+ ImGui::TableSetColumnIndex(0);
+ ImGui::BeginChild("##treeContainer", ImVec2(0, -bottomBarHeight));
+ for (auto node : m_treeCache)
+ m_renderTree(node);
+ ImGui::EndChild();
+
+ // content on the right side
+ ImGui::TableSetColumnIndex(1);
+ ImGui::BeginChild("##contentContainer", ImVec2(0, -bottomBarHeight));
+ m_renderContent();
+ ImGui::EndChild();
+ if (ImGui::IsItemHovered() && ImGui::GetIO().KeyCtrl && ImGui::GetIO().MouseWheel != 0.0f) {
+ m_zoom = std::min<float>(25.0f, std::max<float>(1.0f, m_zoom + ImGui::GetIO().MouseWheel));
+ m_refreshIconPreview();
+ }
+
+ // New file, New directory and Delete popups
+ m_renderPopups();
+
+ ImGui::EndTable();
+ }
+
+
+
+ /***** BOTTOM BAR *****/
+ ImGui::Text("File name:");
+ ImGui::SameLine();
+ if (ImGui::InputTextEx("##file_input", "Filename", m_inputTextbox, 1024, ImVec2((m_type != IFD_DIALOG_DIRECTORY) ? -250.0f : -FLT_MIN, 0), ImGuiInputTextFlags_EnterReturnsTrue)) {
+ bool success = m_finalize(std::string(m_inputTextbox));
+#ifdef _WIN32
+ if (!success)
+ MessageBeep(MB_ICONERROR);
+#else
+ (void)success;
+#endif
+ }
+ if (m_type != IFD_DIALOG_DIRECTORY) {
+ ImGui::SameLine();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ int sel = static_cast<int>(m_filterSelection);
+ if (ImGui::Combo("##ext_combo", &sel, m_filter.c_str())) {
+ m_filterSelection = static_cast<size_t>(sel);
+ m_setDirectory(m_currentDirectory, false); // refresh
+ }
+ }
+
+ // buttons
+ float ok_cancel_width = GUI_ELEMENT_SIZE * 7;
+ ImGui::SetCursorPosX(ImGui::GetWindowWidth() - ok_cancel_width);
+ if (ImGui::Button(m_type == IFD_DIALOG_SAVE ? "Save" : "Open", ImVec2(ok_cancel_width / 2 - ImGui::GetStyle().ItemSpacing.x, 0.0f))) {
+ std::string filename(m_inputTextbox);
+ bool success = false;
+ if (!filename.empty() || m_type == IFD_DIALOG_DIRECTORY)
+ success = m_finalize(filename);
+#ifdef _WIN32
+ if (!success)
+ MessageBeep(MB_ICONERROR);
+#else
+ (void)success;
+#endif
+ }
+ ImGui::SameLine();
+ if (ImGui::Button("Cancel", ImVec2(-FLT_MIN, 0.0f))) {
+ if (m_type == IFD_DIALOG_DIRECTORY)
+ m_isOpen = false;
+ else
+ m_finalize();
+ }
+
+ int escapeKey = ImGui::GetIO().KeyMap[ImGuiKey_Escape];
+ if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
+ escapeKey >= 0 && ImGui::IsKeyPressed((ImGuiKey)escapeKey, (ImGuiID)ImGuiKeyOwner_Any, ImGuiInputFlags_None))
+ m_isOpen = false;
+ }
+}
+
+
+static const unsigned int file_icon[] = {
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x4c000000, 0xf5000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xdd000000, 0x2d000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0xd1000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6a000000, 0xa1000000, 0xff000000, 0xff000000, 0x2e000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x54000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x46000000, 0xf5000000, 0xe0000000, 0xff000000, 0x30000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6e000000, 0xf8000000, 0x01000000, 0xc3000000, 0xff000000, 0x30000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00000000, 0x00000000, 0xd2000000, 0xff000000, 0x30000000, 0x00000000, 0x00000000, 0x00000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x13000000, 0x00000000, 0x00000000, 0xd2000000, 0xff000000, 0x30000000, 0x00000000, 0x00000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x73000000, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xbe000000, 0xff000000, 0x30000000, 0x00000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x65000000, 0xff000000, 0x34000000, 0x10000000, 0x10000000, 0x03000000, 0x0a000000, 0xdb000000, 0xff000000, 0x2f000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0f000000, 0xd9000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xed000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x06000000, 0x5e000000, 0x6c000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x60000000, 0x9e000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x52000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6b000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6b000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0x54000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x54000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0xff000000, 0xd2000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0x6b000000, 0xd2000000, 0xff000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x4c000000, 0xf5000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xf5000000, 0x4b000000, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+};
+static const unsigned int folder_icon[] = {
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00000000, 0x00000000, 0x45000000, 0x8a000000, 0x99000000, 0x97000000, 0x97000000, 0x97000000, 0x97000000, 0x97000000, 0x98000000, 0x81000000, 0x35000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x9e000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x76000000, 0xff000000, 0xff000000, 0xf6000000, 0xe2000000, 0xe2000000, 0xe2000000, 0xe2000000, 0xe2000000, 0xe2000000, 0xe2000000, 0xff000000, 0xff000000, 0xff000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xe7000000, 0xff000000, 0xbe000000, 0x11000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1e000000, 0xd1000000, 0xff000000, 0xff000000, 0x75000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xfa000000, 0xff000000, 0x5a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x06000000, 0xe0000000, 0xff000000, 0xff000000, 0x68000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xf4000000, 0xff000000, 0x67000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x11000000, 0xe4000000, 0xff000000, 0xff000000, 0xad000000, 0x94000000, 0x94000000, 0x94000000, 0x94000000, 0x94000000, 0x94000000, 0x94000000, 0x94000000, 0x94000000, 0x96000000, 0x8b000000, 0x4f000000, 0x00000000, 0x00000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x17000000, 0xe8000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xaf000000, 0x00000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0e000000, 0x88000000, 0xc3000000, 0xcd000000, 0xcc000000, 0xcc000000, 0xcc000000, 0xcc000000, 0xcc000000, 0xcc000000, 0xcc000000, 0xcb000000, 0xcc000000, 0xe2000000, 0xff000000, 0xff000000, 0x81000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xb6000000, 0xff000000, 0xec000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5b000000, 0xff000000, 0xf9000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x68000000, 0xff000000, 0xf4000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf3000000, 0xff000000, 0x6a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6a000000, 0xff000000, 0xf3000000,
+ 0xf4000000, 0xff000000, 0x68000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x68000000, 0xff000000, 0xf4000000,
+ 0xfa000000, 0xff000000, 0x5a000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x5a000000, 0xff000000, 0xf9000000,
+ 0xea000000, 0xff000000, 0xb5000000, 0x05000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x05000000, 0xb5000000, 0xff000000, 0xea000000,
+ 0x7e000000, 0xff000000, 0xff000000, 0xeb000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xd6000000, 0xeb000000, 0xff000000, 0xff000000, 0x7f000000,
+ 0x00000000, 0xac000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xac000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x53000000, 0x8f000000, 0x9a000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x99000000, 0x9a000000, 0x8f000000, 0x53000000, 0x00000000, 0x00000000,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+ 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff,
+};
+const char* ifd::GetDefaultFolderIcon()
+{
+ return (const char*)&folder_icon[0];
+}
+const char* ifd::GetDefaultFileIcon()
+{
+ return (const char*)&file_icon[0];
+}
diff --git a/imfiledialog/ImFileDialog.h b/imfiledialog/ImFileDialog.h
new file mode 100644
index 0000000..76b2123
--- /dev/null
+++ b/imfiledialog/ImFileDialog.h
@@ -0,0 +1,136 @@
+#pragma once
+#include <ctime>
+#include <stack>
+#include <string>
+#include <thread>
+#include <vector>
+#include <functional>
+#include <filesystem>
+#include <unordered_map>
+#include <algorithm> // std::min, std::max
+
+#define IFD_DIALOG_FILE 0
+#define IFD_DIALOG_DIRECTORY 1
+#define IFD_DIALOG_SAVE 2
+
+namespace ifd {
+ class FileDialog {
+ public:
+ static inline FileDialog& Instance()
+ {
+ static FileDialog ret;
+ return ret;
+ }
+
+ FileDialog();
+ ~FileDialog();
+
+ bool Save(const std::string& key, const std::string& title, const std::string& filter, const std::string& startingDir = "");
+
+ bool Open(const std::string& key, const std::string& title, const std::string& filter, bool isMultiselect = false, const std::string& startingDir = "");
+
+ bool IsDone(const std::string& key, int window_w, int window_h);
+
+ inline bool HasResult() { return m_result.size(); }
+ inline const std::filesystem::path& GetResult() { return m_result[0]; }
+ inline const std::vector<std::filesystem::path>& GetResults() { return m_result; }
+
+ void Close();
+
+ inline void SetZoom(float z) {
+ m_zoom = std::min<float>(25.0f, std::max<float>(1.0f, z));
+ m_refreshIconPreview();
+ }
+ inline float GetZoom() { return m_zoom; }
+
+ std::function<void*(uint8_t*, int, int, char)> CreateTexture; // char -> fmt -> { 0 = BGRA, 1 = RGBA }
+ std::function<void(void*)> DeleteTexture;
+
+ class FileTreeNode {
+ public:
+#ifdef _WIN32
+ FileTreeNode(const std::wstring& path) {
+ Path = std::filesystem::path(path);
+ Read = false;
+ }
+#endif
+
+ FileTreeNode(const std::string& path) {
+ Path = std::filesystem::u8path(path);
+ Read = false;
+ }
+
+ std::filesystem::path Path;
+ bool Read;
+ std::vector<FileTreeNode*> Children;
+ };
+ class FileData {
+ public:
+ FileData(const std::filesystem::path& path);
+
+ std::filesystem::path Path;
+ bool IsDirectory;
+ size_t Size;
+ time_t DateModified;
+
+ bool HasIconPreview;
+ void* IconPreview;
+ uint8_t* IconPreviewData;
+ int IconPreviewWidth, IconPreviewHeight;
+ };
+
+ private:
+ std::string m_currentKey;
+ std::string m_currentTitle;
+ std::filesystem::path m_currentDirectory;
+ bool m_isMultiselect;
+ bool m_isOpen;
+ uint8_t m_type;
+ char m_inputTextbox[1024];
+ char m_pathBuffer[1024];
+ char m_newEntryBuffer[1024];
+ char m_searchBuffer[128];
+ bool m_calledOpenPopup;
+ std::stack<std::filesystem::path> m_backHistory, m_forwardHistory;
+ float m_zoom;
+
+ std::vector<std::filesystem::path> m_selections;
+ int m_selectedFileItem;
+ void m_select(const std::filesystem::path& path, bool isCtrlDown = false);
+
+ std::vector<std::filesystem::path> m_result;
+ bool m_finalize(const std::string& filename = "");
+
+ std::string m_filter;
+ std::vector<std::vector<std::string>> m_filterExtensions;
+ size_t m_filterSelection;
+ void m_parseFilter(const std::string& filter);
+
+ std::vector<int> m_iconIndices;
+ std::vector<std::string> m_iconFilepaths; // m_iconIndices[x] <-> m_iconFilepaths[x]
+ std::unordered_map<std::string, void*> m_icons;
+ void* m_getIcon(const std::filesystem::path& path);
+ void m_clearIcons();
+ void m_refreshIconPreview();
+ void m_clearIconPreview();
+
+ std::thread* m_previewLoader;
+ bool m_previewLoaderRunning;
+ void m_stopPreviewLoader();
+ void m_loadPreview();
+
+ std::vector<FileTreeNode*> m_treeCache;
+ void m_clearTree(FileTreeNode* node);
+ void m_renderTree(FileTreeNode* node);
+
+ unsigned int m_sortColumn;
+ unsigned int m_sortDirection;
+ std::vector<FileData> m_content;
+ void m_setDirectory(const std::filesystem::path& p, bool addHistory = true);
+ void m_sortContent(unsigned int column, unsigned int sortDirection);
+ void m_renderContent();
+
+ void m_renderPopups();
+ void m_renderFileDialog();
+ };
+}
diff --git a/imfiledialog/LICENSE b/imfiledialog/LICENSE
new file mode 100644
index 0000000..24cafde
--- /dev/null
+++ b/imfiledialog/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 dfranx
+
+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.