Files
vimix/FileDialog.cpp

1233 lines
29 KiB
C++

/*
* This file is part of vimix - Live video mixer
*
* **Copyright** (C) 2020-2021 Bruno Herbelin <bruno.herbelin@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
**/
#include <fstream>
#include <iostream>
#include <sstream>
#include <stb_image.h>
#include <glad/glad.h>
#ifdef WIN32
#include <include/dirent.h>
#define PATH_SEP '\\'
#ifndef PATH_MAX
#define PATH_MAX 260
#endif
#elif defined(LINUX) or defined(APPLE)
#include <sys/types.h>
#include <dirent.h>
#define PATH_SEP '/'
#endif
#include "imgui.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include "imgui_internal.h"
#include "ImGuiToolkit.h"
#include "FileDialog.h"
static std::string s_fs_root(1u, PATH_SEP);
static std::string currentFileDialog;
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.size() > 0)
{
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.size() > 0 || (token.size() == 0 && pushEmpty))
arr.push_back(token);
start = end + 1;
end = text.find(delimiter, start);
}
arr.push_back(text.substr(start));
}
return arr;
}
inline std::vector<std::string> GetDrivesList()
{
std::vector<std::string> res;
#ifdef WIN32
DWORD mydrives = 2048;
char lpBuffer[2048];
DWORD countChars = GetLogicalDriveStrings(mydrives, lpBuffer);
if (countChars > 0)
{
std::string var = std::string(lpBuffer, countChars);
replaceString(var, "\\", "");
res = splitStringToVector(var, '\0', false);
}
#endif
return res;
}
inline bool IsDirectoryExist(const std::string& name)
{
bool bExists = false;
if (name.size() > 0)
{
DIR *pDir = 0;
pDir = opendir(name.c_str());
if (pDir != NULL)
{
bExists = true;
(void)closedir(pDir);
}
}
return bExists; // this is not a directory!
}
inline bool CreateDirectoryIfNotExist(const std::string& name)
{
bool res = false;
if (name.size() > 0)
{
if (!IsDirectoryExist(name))
{
res = true;
#ifdef WIN32
CreateDirectory(name.c_str(), NULL);
#elif defined(LINUX) or defined(APPLE)
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)
{
std::cout << "Error creating directory " << name << std::endl;
res = false;
}
#endif
}
}
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.size() > 0)
{
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 != "" && st != "\n")
{
replaceString(st, "\n", "");
replaceString(st, "\r", "");
}
vBuffer[slen] = '\0';
std::string str = std::string(vBuffer);
if (str.size() > 0) str += "\n";
str += vStr;
if (len > str.size()) len = str.size();
#ifdef MSVC
strncpy_s(vBuffer, vBufferLen, str.c_str(), len);
#else
strncpy(vBuffer, str.c_str(), len);
#endif
vBuffer[len] = '\0';
}
inline void ResetBuffer(char* vBuffer)
{
vBuffer[0] = '\0';
}
char FileDialog::FileNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = "";
char FileDialog::DirectoryNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = "";
char FileDialog::SearchBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = "";
int FileDialog::FilterIndex = 0;
FileDialog::FileDialog()
{
m_AnyWindowsHovered = false;
IsOk = false;
m_ShowDialog = false;
m_ShowDrives = false;
m_CreateDirectoryMode = false;
dlg_optionsPane = 0;
dlg_optionsPaneWidth = 250;
dlg_filters = "";
}
FileDialog::~FileDialog()
{
}
/* Alphabetical sorting */
/*#ifdef WIN32
static int alphaSort(const void *a, const void *b)
{
return strcoll(((dirent*)a)->d_name, ((dirent*)b)->d_name);
}
#elif defined(LINUX) or defined(APPLE)*/
static int alphaSort(const struct dirent **a, const struct dirent **b)
{
return strcoll((*a)->d_name, (*b)->d_name);
}
//#endif
static bool stringComparator(const FileInfoStruct& a, const FileInfoStruct& b)
{
bool res;
if (a.type != b.type) res = (a.type < b.type);
else res = (a.fileName < b.fileName);
return res;
}
void FileDialog::ScanDir(const std::string& vPath)
{
struct dirent **files = NULL;
std::string path = vPath;
#if defined(LINUX) or defined(APPLE)
if (path.size()>0)
{
if (path[0] != PATH_SEP)
{
//path = PATH_SEP + path;
}
}
#endif
if (0u == m_CurrentPath_Decomposition.size())
{
SetCurrentDir(path);
}
if (0u != m_CurrentPath_Decomposition.size())
{
#ifdef WIN32
if (path == s_fs_root)
{
path += PATH_SEP;
}
#endif
int n = scandir(path.c_str(), &files, NULL, alphaSort);
if (n > 0)
{
m_FileList.clear();
for (int i = 0; i < n; ++i)
{
struct dirent *ent = files[i];
FileInfoStruct infos;
infos.fileName = ent->d_name;
if (("." != infos.fileName)/* && (".." != infos.fileName)*/)
{
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')
{
size_t lpt = infos.fileName.find_last_of('.');
if (lpt != std::string::npos)
{
infos.ext = infos.fileName.substr(lpt);
}
}
bool is_hidden = false;
#ifdef OSWIN
std::string dir = path + std::string(ent->d_name);
// IF system file skip it...
if (FILE_ATTRIBUTE_SYSTEM & GetFileAttributesA(dir.c_str()))
continue;
if (FILE_ATTRIBUTE_HIDDEN & GetFileAttributesA(dir.c_str()))
is_hidden = true;
#else
if(infos.fileName[0] == '.')
is_hidden = true;
#endif // OSWIN
if (!is_hidden)
m_FileList.push_back(infos);
}
}
for (int i = 0; i < n; ++i)
{
free(files[i]);
}
free(files);
}
std::sort(m_FileList.begin(), m_FileList.end(), stringComparator);
}
}
void FileDialog::SetCurrentDir(const std::string& vPath)
{
std::string path = vPath;
#ifdef WIN32
if (s_fs_root == path)
path += PATH_SEP;
#endif
DIR *dir = opendir(path.c_str());
char real_path[PATH_MAX];
if (NULL == dir)
{
path = ".";
dir = opendir(path.c_str());
}
if (NULL != dir)
{
#ifdef WIN32
size_t numchar = GetFullPathName(path.c_str(), PATH_MAX-1, real_path, 0);
#elif defined(LINUX) or defined(APPLE)
char *numchar = realpath(path.c_str(), real_path);
#endif
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);
}
m_CurrentPath_Decomposition = splitStringToVector(m_CurrentPath, PATH_SEP, false);
#if defined(LINUX) or defined(APPLE)
m_CurrentPath_Decomposition.insert(m_CurrentPath_Decomposition.begin(), std::string(1u, PATH_SEP));
#endif
if (m_CurrentPath_Decomposition.size()>0)
{
#ifdef WIN32
s_fs_root = m_CurrentPath_Decomposition[0];
#endif
}
}
closedir(dir);
}
}
bool FileDialog::CreateDir(const std::string& vPath)
{
bool res = false;
if (vPath.size())
{
std::string path = m_CurrentPath + PATH_SEP + vPath;
res = CreateDirectoryIfNotExist(path);
}
return res;
}
void FileDialog::ComposeNewPath(std::vector<std::string>::iterator vIter)
{
m_CurrentPath = "";
while (true)
{
if (!m_CurrentPath.empty())
{
#ifdef WIN32
m_CurrentPath = *vIter + PATH_SEP + m_CurrentPath;
#elif defined(LINUX) or defined(APPLE)
if (*vIter == s_fs_root)
{
m_CurrentPath = *vIter + m_CurrentPath;
}
else
{
m_CurrentPath = *vIter + PATH_SEP + m_CurrentPath;
}
#endif
}
else
{
m_CurrentPath = *vIter;
}
if (vIter == m_CurrentPath_Decomposition.begin())
{
#if defined(LINUX) or defined(APPLE)
if (m_CurrentPath[0] != PATH_SEP)
m_CurrentPath = PATH_SEP + m_CurrentPath;
#endif
break;
}
--vIter;
}
}
void FileDialog::GetDrives()
{
auto res = GetDrivesList();
if (res.size() > 0)
{
m_CurrentPath = "";
m_CurrentPath_Decomposition.clear();
m_FileList.clear();
for (auto it = res.begin(); it != res.end(); ++it)
{
FileInfoStruct infos;
infos.fileName = *it;
infos.type = 'd';
if (infos.fileName.size() > 0)
{
m_FileList.push_back(infos);
}
}
m_ShowDrives = true;
}
}
void FileDialog::OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
const std::string& vPath, const std::string& vDefaultFileName,
std::function<void(std::string, bool*)> vOptionsPane, size_t vOptionsPaneWidth, const std::string& vUserString)
{
if (m_ShowDialog) // si deja ouvert on ne fou pas la merde en voulant en ecrire une autre
return;
dlg_key = vKey;
dlg_name = std::string(vName);
dlg_filters = vFilters;
dlg_path = vPath;
dlg_defaultFileName = vDefaultFileName;
dlg_optionsPane = vOptionsPane;
dlg_userString = vUserString;
dlg_optionsPaneWidth = vOptionsPaneWidth;
dlg_defaultExt = "";
m_ShowDialog = true;
}
void FileDialog::OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
const std::string& vFilePathName,
std::function<void(std::string, bool*)> vOptionsPane, size_t vOptionsPaneWidth, const std::string& vUserString)
{
if (m_ShowDialog) // si deja ouvert on ne fou pas la merde en voulant en ecrire une autre
return;
dlg_key = vKey;
dlg_name = std::string(vName);
dlg_filters = vFilters;
auto ps = ParsePathFileName(vFilePathName);
if (ps.isOk)
{
dlg_path = ps.path;
dlg_defaultFileName = vFilePathName;
dlg_defaultExt = "." + ps.ext;
}
else
{
dlg_path = ".";
dlg_defaultFileName = "";
dlg_defaultExt = "";
}
dlg_optionsPane = vOptionsPane;
dlg_userString = vUserString;
dlg_optionsPaneWidth = vOptionsPaneWidth;
m_ShowDialog = true;
}
void FileDialog::OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
const std::string& vFilePathName, const std::string& vUserString)
{
if (m_ShowDialog) // si deja ouvert on ne fou pas la merde en voulant en ecrire une autre
return;
dlg_key = vKey;
dlg_name = std::string(vName);
dlg_filters = vFilters;
auto ps = ParsePathFileName(vFilePathName);
if (ps.isOk)
{
dlg_path = ps.path;
dlg_defaultFileName = vFilePathName;
dlg_defaultExt = "." + ps.ext;
}
else
{
dlg_path = ".";
dlg_defaultFileName = "";
dlg_defaultExt = "";
}
dlg_optionsPane = 0;
dlg_userString = vUserString;
dlg_optionsPaneWidth = 0;
m_ShowDialog = true;
}
void FileDialog::OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
const std::string& vPath, const std::string& vDefaultFileName, const std::string& vUserString)
{
if (m_ShowDialog) // si deja ouvert on ne fou pas la merde en voulant en ecrire une autre
return;
dlg_key = vKey;
dlg_name = std::string(vName);
dlg_filters = vFilters;
dlg_path = vPath;
dlg_defaultFileName = vDefaultFileName;
dlg_optionsPane = 0;
dlg_userString = vUserString;
dlg_optionsPaneWidth = 0;
dlg_defaultExt = "";
m_ShowDialog = true;
}
void FileDialog::CloseDialog(const std::string& vKey)
{
if (dlg_key == vKey)
{
dlg_key.clear();
m_ShowDialog = false;
}
}
void FileDialog::SetPath(const std::string& vPath)
{
m_ShowDrives = false;
m_CurrentPath = vPath;
m_FileList.clear();
m_CurrentPath_Decomposition.clear();
ScanDir(m_CurrentPath);
}
bool FileDialog::Render(const std::string& vKey, ImVec2 geometry)
{
std::string name = dlg_name + "##" + dlg_key;
if (m_ShowDialog) {
ImGui::OpenPopup(name.c_str());
// m_ShowDialog = false;
}
if (dlg_key == vKey)
{
bool res = false;
if (m_Name != name)
{
m_FileList.clear();
m_CurrentPath_Decomposition.clear();
}
IsOk = false;
if (ImGui::BeginPopupModal(name.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize))
{
m_Name = name;
m_AnyWindowsHovered |= ImGui::IsWindowHovered();
if (dlg_path.size() == 0) dlg_path = ".";
if (m_FileList.size() == 0 && !m_ShowDrives)
{
replaceString(dlg_defaultFileName, dlg_path, ""); // local path
if (dlg_defaultFileName.size() > 0)
{
ResetBuffer(FileNameBuffer);
AppendToBuffer(FileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, dlg_defaultFileName);
m_SelectedFileName = dlg_defaultFileName;
if (dlg_defaultExt.size() > 0)
{
m_SelectedExt = dlg_defaultExt;
FileDialog::FilterIndex = 0;
size_t size = 0;
const char* p = dlg_filters; // FIXME-OPT: Avoid computing this, or at least only when combo is open
while (*p)
{
size += strlen(p) + 1;
p += size;
}
int idx = 0;
auto arr = splitStringToVector(std::string(dlg_filters, size), '\0', false);
for (auto it = arr.begin(); it != arr.end(); ++it)
{
if (m_SelectedExt == *it)
{
FileDialog::FilterIndex = idx;
break;
}
idx++;
}
}
}
ScanDir(dlg_path);
}
if (ImGuiToolkit::ButtonIcon(3, 8))
// if (ImGui::Button("+")) // new Folder
{
if (!m_CreateDirectoryMode)
{
m_CreateDirectoryMode = true;
ResetBuffer(DirectoryNameBuffer);
}
}
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("OK"))
{
std::string newDir = std::string(DirectoryNameBuffer);
if (CreateDir(newDir))
{
SetPath(m_CurrentPath + PATH_SEP + newDir);
}
m_CreateDirectoryMode = false;
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
m_CreateDirectoryMode = false;
}
}
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
// if (ImGui::Button("R")) // HOME
if (ImGuiToolkit::ButtonIcon(2,10))
{
SetPath("."); // Go Home
}
#ifdef WIN32
bool drivesClick = false;
ImGui::SameLine();
if (ImGui::Button("Drives"))
{
drivesClick = true;
}
#endif
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
// show current path
bool pathClick = false;
if (m_CurrentPath_Decomposition.size() > 0)
{
ImGui::SameLine();
for (std::vector<std::string>::iterator itPathDecomp = m_CurrentPath_Decomposition.begin();
itPathDecomp != m_CurrentPath_Decomposition.end(); ++itPathDecomp)
{
if (itPathDecomp != m_CurrentPath_Decomposition.begin())
ImGui::SameLine();
if (ImGui::Button((*itPathDecomp).c_str()))
{
ComposeNewPath(itPathDecomp);
pathClick = true;
break;
}
}
}
// search field
ImGuiToolkit::Icon(14, 12);
ImGui::SameLine();
if (ImGui::InputText("##ImGuiFileDialogSearchFiled", SearchBuffer, MAX_FILE_DIALOG_NAME_BUFFER))
{
searchTag = SearchBuffer;
}
// reset search
ImGui::SameLine();
if (ImGuiToolkit::ButtonIcon(13, 2))
{
ResetBuffer(SearchBuffer);
searchTag.clear();
}
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
ImVec2 dsize = geometry - ImVec2((float)dlg_optionsPaneWidth, 180.0f);
ImGui::BeginChild("##FileDialog_FileList", dsize);
for (std::vector<FileInfoStruct>::iterator it = m_FileList.begin(); it != m_FileList.end(); ++it)
{
const FileInfoStruct& infos = *it;
bool show = true;
std::string str;
if (infos.type == 'd') str = "[Dir] " + infos.fileName;
if (infos.type == 'l') str = "[Link] " + infos.fileName;
if (infos.type == 'f') str = "[File] " + infos.fileName;
if (infos.type == 'f' && m_SelectedExt.size() > 0 && (infos.ext != m_SelectedExt && m_SelectedExt != ".*"))
{
show = false;
}
if (searchTag.size() > 0 && infos.fileName.find(searchTag) == std::string::npos)
{
show = false;
}
if (show == true)
{
ImVec4 c;
bool showColor = false;
if (infos.type == 'd') { ImGuiToolkit::Icon(5, 8); showColor=true; c = ImVec4(1.0, 1.0, 0.0, 1.0); };
if (infos.type == 'l') { ImGuiToolkit::Icon(18, 13); showColor=true; c = ImVec4(1.0, 1.0, 1.0, 0.6); };
if (infos.type == 'f') {
showColor = GetFilterColor(infos.ext, &c);
if (showColor) ImGuiToolkit::Icon(14, 7); else ImGuiToolkit::Icon(8, 8);
}
if (showColor)
ImGui::PushStyleColor(ImGuiCol_Text, c);
ImGui::SameLine();
if (ImGui::Selectable(infos.fileName.c_str(), (infos.fileName == m_SelectedFileName)))
{
if (infos.type == 'd')
{
if ((*it).fileName == "..")
{
if (m_CurrentPath_Decomposition.size() > 1)
{
ComposeNewPath(m_CurrentPath_Decomposition.end() - 2);
pathClick = true;
}
}
else
{
std::string newPath;
if (m_ShowDrives)
{
newPath = infos.fileName + PATH_SEP;
}
else
{
#ifdef LINUX
if (s_fs_root == m_CurrentPath)
{
newPath = m_CurrentPath + infos.fileName;
}
else
{
#endif
newPath = m_CurrentPath + PATH_SEP + infos.fileName;
#ifdef LINUX
}
#endif
}
if (IsDirectoryExist(newPath))
{
if (m_ShowDrives)
{
m_CurrentPath = infos.fileName;
s_fs_root = m_CurrentPath;
}
else
{
m_CurrentPath = newPath;
}
pathClick = true;
}
}
}
else
{
m_SelectedFileName = infos.fileName;
ResetBuffer(FileNameBuffer);
AppendToBuffer(FileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, m_SelectedFileName);
}
if (showColor)
ImGui::PopStyleColor();
break;
}
if (showColor)
ImGui::PopStyleColor();
}
}
// changement de repertoire
if (pathClick == true)
{
SetPath(m_CurrentPath);
}
#ifdef WIN32
if (drivesClick == true)
{
GetDrives();
}
#endif
ImGui::EndChild();
ImGui::PopFont();
bool _CanWeContinue = true;
if (dlg_optionsPane)
{
ImGui::SameLine();
dsize.x = (float)dlg_optionsPaneWidth;
ImGui::BeginChild("##FileTypes", dsize);
dlg_optionsPane(m_SelectedExt, &_CanWeContinue);
ImGui::EndChild();
}
ImGui::Text("File Name : ");
ImGui::SameLine();
float width = ImGui::GetContentRegionAvail().x;
if (dlg_filters) width -= 120.0f;
ImGui::PushItemWidth(width);
ImGui::InputText("##FileName", FileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
ImGui::PopItemWidth();
if (dlg_filters)
{
ImGui::SameLine();
ImGui::PushItemWidth(100.0f);
bool comboClick = ImGui::Combo("##Filters", &FilterIndex, dlg_filters) || m_SelectedExt.size() == 0;
ImGui::PopItemWidth();
if (comboClick == true)
{
int itemIdx = 0;
const char* p = dlg_filters;
while (*p)
{
if (FilterIndex == itemIdx)
{
m_SelectedExt = std::string(p);
break;
}
p += strlen(p) + 1;
itemIdx++;
}
}
}
//width = ImGui::GetContentRegionAvail().x;
float button_width = 200.f;
if (ImGui::Button(" Cancel ", ImVec2(button_width, 0)))
{
IsOk = false;
res = true;
}
if (_CanWeContinue)
{
ImGui::SameLine();
float w = ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x;
ImGui::Dummy(ImVec2(w - button_width, 0)); ImGui::SameLine(); // right align
if (ImGui::Button(" Ok ", ImVec2(button_width, 0)))
{
if ('\0' != FileNameBuffer[0])
{
IsOk = true;
res = true;
}
}
ImGui::SetItemDefaultFocus();
}
if(res) {
ImGui::CloseCurrentPopup();
m_ShowDialog = false;
}
ImGui::EndPopup();
}
// ImGui::End();
if (res == true)
{
m_FileList.clear();
}
return res;
}
return false;
}
std::string FileDialog::GetFilepathName()
{
std::string result = m_CurrentPath;
if (s_fs_root != result)
{
result += PATH_SEP;
}
result += GetCurrentFileName();
return result;
}
std::string FileDialog::GetCurrentPath()
{
return m_CurrentPath;
}
std::string FileDialog::GetCurrentFileName()
{
std::string result = FileNameBuffer;
// size_t lastPoint = result.find_last_of('.');
// if (lastPoint != std::string::npos)
// {
// result = result.substr(0, lastPoint);
// }
// result += m_SelectedExt;
return result;
}
std::string FileDialog::GetCurrentFilter()
{
return m_SelectedExt;
}
std::string FileDialog::GetUserString()
{
return dlg_userString;
}
void FileDialog::SetFilterColor(const std::string &vFilter, ImVec4 vColor)
{
m_FilterColor[vFilter] = vColor;
}
bool FileDialog::GetFilterColor(const std::string &vFilter, ImVec4 *vColor)
{
if (vColor)
{
if (m_FilterColor.find(vFilter) != m_FilterColor.end()) // found
{
*vColor = m_FilterColor[vFilter];
return true;
}
}
return false;;
}
void FileDialog::ClearFilterColor()
{
m_FilterColor.clear();
}
// template info panel
inline void InfosPane(std::string vFilter, bool *vCantContinue)
// if vCantContinue is false, the user cant validate the dialog
{
ImGui::TextColored(ImVec4(0, 1, 1, 1), "Infos Pane");
ImGui::Text("Selected Filter : %s", vFilter.c_str());
// File Manager information pannel
bool canValidateDialog = false;
ImGui::Checkbox("if not checked you cant validate the dialog", &canValidateDialog);
if (vCantContinue)
*vCantContinue = canValidateDialog;
}
inline void TextInfosPane(const std::string &vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
{
ImGui::TextColored(ImVec4(0, 1, 1, 1), "Text");
static std::string filepathcurrent;
static std::string text;
// if different file selected
if ( filepathcurrent.compare(FileDialog::Instance()->GetFilepathName()) != 0)
{
filepathcurrent = FileDialog::Instance()->GetFilepathName();
// fill text with file content
std::ifstream textfilestream(filepathcurrent, std::ios::in);
if(textfilestream.is_open()){
std::stringstream sstr;
sstr << textfilestream.rdbuf();
text = sstr.str();
textfilestream.close();
}
else
{
// empty text
text = "";
}
}
// show text
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 340);
ImGui::Text("%s", text.c_str());
// release Ok button if text is not empty
if (vCantContinue)
*vCantContinue = text.size() > 0;
}
inline void ImageInfosPane(const std::string &vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
{
// opengl texture
static GLuint tex = 0;
static std::string filepathcurrent;
static std::string message = "Please select an image (" + vFilter + ").";
static unsigned char* img = NULL;
static ImVec2 image_size(330, 330);
// generate texture (once) & clear
if (tex == 0) {
glGenTextures(1, &tex);
glBindTexture( GL_TEXTURE_2D, tex);
unsigned char clearColor[4] = {0};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
}
// if different file selected
if ( filepathcurrent.compare(FileDialog::Instance()->GetFilepathName()) != 0)
{
// remember path
filepathcurrent = FileDialog::Instance()->GetFilepathName();
// prepare texture
glBindTexture( GL_TEXTURE_2D, tex);
// load image
int w, h, n;
img = stbi_load(filepathcurrent.c_str(), &w, &h, &n, 4);
if (img != NULL) {
// apply img to texture
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
// adjust image display aspect ratio
image_size.y = image_size.x * static_cast<float>(h) / static_cast<float>(w);
// free loaded image
stbi_image_free(img);
message = FileDialog::Instance()->GetCurrentFileName() + "(" + std::to_string(w) + "x" + std::to_string(h) + ")";
}
else {
// clear image
unsigned char clearColor[4] = {0};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
message = "Please select an image (" + vFilter + ").";
}
}
// draw text and image
ImGui::TextColored(ImVec4(0, 1, 1, 1), "%s", message.c_str());
ImGui::Image((void*)(intptr_t)tex, image_size);
// release Ok button if image is not null
if (vCantContinue)
*vCantContinue = img!=NULL;
}
void FileDialog::RenderCurrent()
{
ImVec2 geometry(1200.0, 640.0);
if ( !currentFileDialog.empty() && FileDialog::Instance()->Render(currentFileDialog.c_str(), geometry)) {
if (FileDialog::Instance()->IsOk == true) {
std::string filePathNameText = FileDialog::Instance()->GetFilepathName();
// done
currentFileDialog = "";
}
FileDialog::Instance()->CloseDialog(currentFileDialog.c_str());
}
}
void FileDialog::SetCurrentOpenText()
{
currentFileDialog = "ChooseFileText";
FileDialog::Instance()->ClearFilterColor();
FileDialog::Instance()->SetFilterColor(".cpp", ImVec4(1,1,0,0.5));
FileDialog::Instance()->SetFilterColor(".h", ImVec4(0,1,0,0.5));
FileDialog::Instance()->SetFilterColor(".hpp", ImVec4(0,0,1,0.5));
FileDialog::Instance()->OpenDialog(currentFileDialog.c_str(), "Open Text File", ".cpp\0.h\0.hpp\0\0", ".", "",
std::bind(&TextInfosPane, std::placeholders::_1, std::placeholders::_2), 350, "Text info");
}
void FileDialog::SetCurrentOpenImage()
{
currentFileDialog = "ChooseFileImage";
FileDialog::Instance()->ClearFilterColor();
FileDialog::Instance()->SetFilterColor(".png", ImVec4(0,1,1,1.0));
FileDialog::Instance()->SetFilterColor(".jpg", ImVec4(0,1,1,1.0));
FileDialog::Instance()->SetFilterColor(".gif", ImVec4(0,1,1,1.0));
FileDialog::Instance()->OpenDialog(currentFileDialog.c_str(), "Open Image File", ".*\0.png\0.jpg\0.gif\0\0", ".", "",
std::bind(&ImageInfosPane, std::placeholders::_1, std::placeholders::_2), 350, "Image info");
}
void FileDialog::SetCurrentOpenMedia()
{
currentFileDialog = "ChooseFileMedia";
FileDialog::Instance()->ClearFilterColor();
FileDialog::Instance()->SetFilterColor(".mp4", ImVec4(0,1,1,1.0));
FileDialog::Instance()->SetFilterColor(".avi", ImVec4(0,1,1,1.0));
FileDialog::Instance()->SetFilterColor(".mov", ImVec4(0,1,1,1.0));
FileDialog::Instance()->OpenDialog(currentFileDialog.c_str(), "Open Media File", ".*\0.mp4\0.avi\0.mov\0\0", ".", "", "Media");
}