// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

/*
MIT License

Copyright (c) 2019-2020 Stephane Cuillerdier (aka aiekick)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef IMGUI_DEFINE_MATH_OPERATORS
  #define IMGUI_DEFINE_MATH_OPERATORS
#endif // IMGUI_DEFINE_MATH_OPERATORS

#include "ImGuiFileDialog.h"
#include "../../src/ta-log.h"

#ifdef __cplusplus

#include <cfloat>
#include <cstring> // stricmp / strcasecmp
#include <cstdarg> // variadic
#include <sstream>
#include <iomanip>
#include <ctime>
#include <sys/stat.h>
#include <cstdio>
#if defined (__EMSCRIPTEN__) // EMSCRIPTEN
  #include <emscripten.h>
#endif // EMSCRIPTEN
#ifdef WIN32
  #define stricmp _stricmp
  #include <cctype>
  #include "dirent/dirent.h" // directly open the dirent file attached to this lib
  #define PATH_SEP '\\'
  #define PATH_SEP_STR "\\"
  #ifndef PATH_MAX
    #define PATH_MAX 260
  #endif // PATH_MAX
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined (__EMSCRIPTEN__) || defined(__HAIKU__)
  #define UNIX
  #define stricmp strcasecmp
  #include <sys/types.h>
  #include <dirent.h>
  #define PATH_SEP '/'
  #define PATH_SEP_STR "/"
#endif // defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)

#include "imgui.h"
#include "imgui_internal.h"
#include <IconsFontAwesome4.h>

#include <cstdlib>
#include <algorithm>
#include <iostream>

#define DOUBLE_CLICKED ((singleClickSel && ImGui::IsMouseReleased(0)) || (!singleClickSel && ImGui::IsMouseDoubleClicked(0)))

#ifdef USE_THUMBNAILS
#ifndef DONT_DEFINE_AGAIN__STB_IMAGE_IMPLEMENTATION
#ifndef STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#endif // STB_IMAGE_IMPLEMENTATION
#endif // DONT_DEFINE_AGAIN__STB_IMAGE_IMPLEMENTATION
#include "stb/stb_image.h"
#ifndef DONT_DEFINE_AGAIN__STB_IMAGE_RESIZE_IMPLEMENTATION
#ifndef STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#endif // STB_IMAGE_RESIZE_IMPLEMENTATION
#endif // DONT_DEFINE_AGAIN__STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb/stb_image_resize.h"
#endif // USE_THUMBNAILS

namespace IGFD
{
// float comparisons
#ifndef IS_FLOAT_DIFFERENT
#define IS_FLOAT_DIFFERENT(a,b) (fabs((a) - (b)) > FLT_EPSILON)
#endif // IS_FLOAT_DIFFERENT
#ifndef IS_FLOAT_EQUAL
#define IS_FLOAT_EQUAL(a,b) (fabs((a) - (b)) < FLT_EPSILON)
#endif // IS_FLOAT_EQUAL
// width of filter combobox
#ifndef FILTER_COMBO_WIDTH
#define FILTER_COMBO_WIDTH 150.0f
#endif // FILTER_COMBO_WIDTH
// for lets you define your button widget
// if you have like me a special bi-color button
#ifndef IMGUI_PATH_BUTTON
#define IMGUI_PATH_BUTTON ImGui::Button
#endif // IMGUI_PATH_BUTTON
#ifndef IMGUI_BUTTON
#define IMGUI_BUTTON ImGui::Button
#endif // IMGUI_BUTTON
// locales
#ifndef createDirButtonString
#define createDirButtonString ICON_FA_PLUS
#endif // createDirButtonString
#ifndef resetButtonString
#define resetButtonString ICON_FA_REPEAT
#endif // resetButtonString
#ifndef homeButtonString
#define homeButtonString ICON_FA_HOME
#endif // bomeButtonString
#ifndef drivesButtonString
#define drivesButtonString ICON_FA_HDD_O
#endif // drivesButtonString
#ifndef parentDirString
#define parentDirString ICON_FA_CHEVRON_UP
#endif // parentDirString
#ifndef editPathButtonString
#define editPathButtonString ICON_FA_PENCIL
#endif // editPathButtonString
#ifdef USE_THUMBNAILS
#ifndef tableHeaderFileThumbnailsString
#define tableHeaderFileThumbnailsString "Thumbnails"
#endif // tableHeaderFileThumbnailsString
#ifndef DisplayMode_FilesList_ButtonString
#define DisplayMode_FilesList_ButtonString "FL"
#endif // DisplayMode_FilesList_ButtonString
#ifndef DisplayMode_FilesList_ButtonHelp
#define DisplayMode_FilesList_ButtonHelp "File List"
#endif // DisplayMode_FilesList_ButtonHelp
#ifndef DisplayMode_ThumbailsList_ButtonString
#define DisplayMode_ThumbailsList_ButtonString "TL"
#endif // DisplayMode_ThumbailsList_ButtonString
#ifndef DisplayMode_ThumbailsList_ButtonHelp
#define DisplayMode_ThumbailsList_ButtonHelp "Thumbnails List"
#endif // DisplayMode_ThumbailsList_ButtonHelp
#ifndef DisplayMode_ThumbailsGrid_ButtonString
#define DisplayMode_ThumbailsGrid_ButtonString "TG"
#endif // DisplayMode_ThumbailsGrid_ButtonString
#ifndef DisplayMode_ThumbailsGrid_ButtonHelp
#define DisplayMode_ThumbailsGrid_ButtonHelp "Thumbnails Grid"
#endif // DisplayMode_ThumbailsGrid_ButtonHelp
#ifndef DisplayMode_ThumbailsList_ImageHeight 
#define DisplayMode_ThumbailsList_ImageHeight 32.0f
#endif // DisplayMode_ThumbailsList_ImageHeight
#ifndef IMGUI_RADIO_BUTTON
  inline bool inRadioButton(const char* vLabel, bool vToggled)
  {
    bool pressed = false;

    if (vToggled)
    {
      ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
      ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text);
      ImGui::PushStyleColor(ImGuiCol_Button, te);
      ImGui::PushStyleColor(ImGuiCol_ButtonActive, te);
      ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te);
      ImGui::PushStyleColor(ImGuiCol_Text, bua);
    }

    pressed = IMGUI_BUTTON(vLabel);

    if (vToggled)
    {
      ImGui::PopStyleColor(4); //-V112
    }

    return pressed;
  }
#define IMGUI_RADIO_BUTTON inRadioButton
#endif // IMGUI_RADIO_BUTTON
#endif  // USE_THUMBNAILS
#ifdef USE_BOOKMARK
#ifndef defaultBookmarkPaneWith
#define defaultBookmarkPaneWith 150.0f
#endif // defaultBookmarkPaneWith
#ifndef bookmarksButtonString
#define bookmarksButtonString "Bookmark"
#endif // bookmarksButtonString
#ifndef bookmarksButtonHelpString
#define bookmarksButtonHelpString "Bookmark"
#endif // bookmarksButtonHelpString
#ifndef addBookmarkButtonString
#define addBookmarkButtonString "+"
#endif // addBookmarkButtonString
#ifndef removeBookmarkButtonString
#define removeBookmarkButtonString "-"
#endif // removeBookmarkButtonString
#ifndef IMGUI_TOGGLE_BUTTON
  inline bool inToggleButton(const char* vLabel, bool* vToggled)
  {
    bool pressed = false;

    if (vToggled && *vToggled)
    {
      ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
      //ImVec4 buh = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);
      //ImVec4 bu = ImGui::GetStyleColorVec4(ImGuiCol_Button);
      ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text);
      ImGui::PushStyleColor(ImGuiCol_Button, te);
      ImGui::PushStyleColor(ImGuiCol_ButtonActive, te);
      ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te);
      ImGui::PushStyleColor(ImGuiCol_Text, bua);
    }

    pressed = IMGUI_BUTTON(vLabel);

    if (vToggled && *vToggled)
    {
      ImGui::PopStyleColor(4); //-V112
    }

    if (vToggled && pressed)
      *vToggled = !*vToggled;

    return pressed;
  }
#define IMGUI_TOGGLE_BUTTON inToggleButton
#endif // IMGUI_TOGGLE_BUTTON
#endif // USE_BOOKMARK

  /////////////////////////////////////////////////////////////////////////////////////
  //// INLINE FUNCTIONS ///////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  inline int inAlphaSort(const struct dirent** a, const struct dirent** b)
  {
    return strcoll((*a)->d_name, (*b)->d_name);
  }

  /////////////////////////////////////////////////////////////////////////////////////
  //// FILE EXTENTIONS INFOS //////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  IGFD::FileStyle::FileStyle() 
    : color(0, 0, 0, 0)
  { 

  }
  
  IGFD::FileStyle::FileStyle(const FileStyle& vStyle)
  {
    color = vStyle.color;
    icon = vStyle.icon;
    font = vStyle.font;
    flags = vStyle.flags;
  }

  IGFD::FileStyle::FileStyle(const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) 
    : color(vColor), icon(vIcon), font(vFont)
  { 

  }

  /////////////////////////////////////////////////////////////////////////////////////
  //// FILE INFOS /////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  // https://github.com/ocornut/imgui/issues/1720
  bool IGFD::Utils::Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size)
  {
    using namespace ImGui;
    ImGuiContext& g = *GImGui;
    ImGuiWindow* window = g.CurrentWindow;
    ImGuiID id = window->GetID("##Splitter");
    ImRect bb;
    bb.Min = window->DC.CursorPos + (split_vertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1));
    bb.Max = bb.Min + CalcItemSize(split_vertically ? ImVec2(thickness, splitter_long_axis_size) : ImVec2(splitter_long_axis_size, thickness), 0.0f, 0.0f);
    return SplitterBehavior(bb, id, split_vertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, min_size1, min_size2, 1.0f);
  }

#ifdef WIN32
  bool IGFD::Utils::WReplaceString(std::wstring& str, const std::wstring& oldStr, const std::wstring& newStr)
  {
    bool found = false;
    size_t pos = 0;
    while ((pos = str.find(oldStr, pos)) != std::wstring::npos)
    {
      found = true;
      str.replace(pos, oldStr.length(), newStr);
      pos += newStr.length();
    }
    return found;
  }

  std::vector<std::wstring> IGFD::Utils::WSplitStringToVector(const std::wstring& text, char delimiter, bool pushEmpty)
  {
    std::vector<std::wstring> arr;
    if (!text.empty())
    {
      std::wstring::size_type start = 0;
      std::wstring::size_type end = text.find(delimiter, start);
      while (end != std::wstring::npos)
      {
        std::wstring token = text.substr(start, end - start);
        if (!token.empty() || (token.empty() && pushEmpty)) //-V728
          arr.push_back(token);
        start = end + 1;
        end = text.find(delimiter, start);
      }
      std::wstring token = text.substr(start);
      if (!token.empty() || (token.empty() && pushEmpty)) //-V728
        arr.push_back(token);
    }
    return arr;
  }

  std::wstring IGFD::Utils::string_to_wstring(const std::string& str)
  {
    std::wstring ret;
    if (!str.empty())
    {
      size_t sz = std::mbstowcs(nullptr, str.c_str(), str.size());
      if (sz)
      {
        ret.resize(sz);
        std::mbstowcs((wchar_t*)ret.data(), str.c_str(), sz);
      }
    }
    return ret;
  }

  std::string IGFD::Utils::wstring_to_string(const std::wstring& str)
  {
    std::string ret;
    if (!str.empty())
    {
      size_t sz = std::wcstombs(nullptr, str.c_str(), str.size());
      if (sz)
      {
        ret.resize(sz);
        std::wcstombs((char*)ret.data(), str.c_str(), sz);
      }
    }
    return ret;
  }
#endif // WIN32

  bool IGFD::Utils::ReplaceString(std::string& str, const std::string& oldStr, const std::string& newStr)
  {
    bool found = false;
    size_t pos = 0;
    while ((pos = str.find(oldStr, pos)) != std::string::npos)
    {
      found = true;
      str.replace(pos, oldStr.length(), newStr);
      pos += newStr.length();
    }
    return found;
  }

  std::vector<std::string> IGFD::Utils::SplitStringToVector(const std::string& text, char delimiter, bool pushEmpty)
  {
    std::vector<std::string> arr;
    if (!text.empty())
    {
      size_t start = 0;
      size_t end = text.find(delimiter, start);
      while (end != std::string::npos)
      {
        auto token = text.substr(start, end - start);
        if (!token.empty() || (token.empty() && pushEmpty)) //-V728
          arr.push_back(token);
        start = end + 1;
        end = text.find(delimiter, start);
      }
      auto token = text.substr(start);
      if (!token.empty() || (token.empty() && pushEmpty)) //-V728
        arr.push_back(token);
    }
    return arr;
  }

  std::vector<std::string> IGFD::Utils::GetDrivesList()
  {
    std::vector<std::string> res;

#ifdef WIN32
    const DWORD mydrives = 2048;
    char lpBuffer[2048];
#define mini(a,b) (((a) < (b)) ? (a) : (b))
    const DWORD countChars = mini(GetLogicalDriveStringsA(mydrives, lpBuffer), 2047);
#undef mini
    if (countChars > 0)
    {
      std::string var = std::string(lpBuffer, (size_t)countChars);
      IGFD::Utils::ReplaceString(var, "\\", "");
      res = IGFD::Utils::SplitStringToVector(var, '\0', false);
    }
#endif // WIN32

    return res;
  }

  bool IGFD::Utils::IsDirectoryExist(const std::string& name)
  {
    bool bExists = false;

    if (!name.empty())
    {
      DIR* pDir = nullptr;
      pDir = opendir(name.c_str());
      if (pDir != nullptr)
      {
        bExists = true;
        (void)closedir(pDir);
      }
    }

    return bExists;    // this is not a directory!
  }

  bool IGFD::Utils::CreateDirectoryIfNotExist(const std::string& name)
  {
    bool res = false;

    if (!name.empty())
    {
      if (!IsDirectoryExist(name))
      {
#ifdef WIN32
        std::wstring wname = IGFD::Utils::string_to_wstring(name);
        if (CreateDirectoryW(wname.c_str(), nullptr))
        {
          res = true;
        }
#elif defined(__EMSCRIPTEN__)
        std::string str = std::string("FS.mkdir('") + name + "');";
        emscripten_run_script(str.c_str());
        res = true;
#elif defined(UNIX)
        char buffer[PATH_MAX] = {};
        snprintf(buffer, PATH_MAX, "mkdir -p %s", name.c_str());
        const int dir_err = std::system(buffer);
        if (dir_err != -1)
        {
          res = true;
        }
#endif // WIN32
        if (!res) {
          std::cout << "Error creating directory " << name << std::endl;
        }
      }
    }

    return res;
  }

  IGFD::Utils::PathStruct IGFD::Utils::ParsePathFileName(const std::string& vPathFileName)
  {
    PathStruct res;

    if (!vPathFileName.empty())
    {
      std::string pfn = vPathFileName;
      std::string separator(1u, PATH_SEP);
      IGFD::Utils::ReplaceString(pfn, "\\", separator);
      IGFD::Utils::ReplaceString(pfn, "/", separator);

      size_t lastSlash = pfn.find_last_of(separator);
      if (lastSlash != std::string::npos)
      {
        res.name = pfn.substr(lastSlash + 1);
        res.path = pfn.substr(0, lastSlash);
        res.isOk = true;
      }

      size_t lastPoint = pfn.find_last_of('.');
      if (lastPoint != std::string::npos)
      {
        if (!res.isOk)
        {
          res.name = pfn;
          res.isOk = true;
        }
        res.ext = pfn.substr(lastPoint + 1);
        IGFD::Utils::ReplaceString(res.name, "." + res.ext, "");
      }
      
      if (res.path.empty()) {
        res.path=separator;
      }

      if (!res.isOk)
      {
        res.name = std::move(pfn);
        res.isOk = true;
      }
    }

    return res;
  }
  void IGFD::Utils::AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr)
  {
    std::string st = vStr;
    size_t len = vBufferLen - 1u;
    size_t slen = strlen(vBuffer);

    if (!st.empty() && st != "\n")
    {
      IGFD::Utils::ReplaceString(st, "\n", "");
      IGFD::Utils::ReplaceString(st, "\r", "");
    }
    vBuffer[slen] = '\0';
    std::string str = std::string(vBuffer);
    //if (!str.empty()) str += "\n";
    str += vStr;
    if (len > str.size()) len = str.size();
#ifdef MSVC
    strncpy_s(vBuffer, vBufferLen, str.c_str(), len);
#else // MSVC
    strncpy(vBuffer, str.c_str(), len);
#endif // MSVC
    vBuffer[len] = '\0';
  }

  void IGFD::Utils::ResetBuffer(char* vBuffer)
  {
    vBuffer[0] = '\0';
  }

  void IGFD::Utils::SetBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr)
  {
    ResetBuffer(vBuffer);
    AppendToBuffer(vBuffer, vBufferLen, vStr);
  }

  /////////////////////////////////////////////////////////////////////////////////////
  //// FILE INFOS /////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  bool IGFD::FileInfos::IsTagFound(const std::string& vTag) const
  {
    if (!vTag.empty())
    {
      if (fileNameExt_optimized == "..") return true;

      return
        fileNameExt_optimized.find(vTag) != std::string::npos ||  // first try wihtout case and accents
        fileNameExt.find(vTag) != std::string::npos;        // second if searched with case and accents
    }

    // if tag is empty => its a special case but all is found
    return true;
  }

  /////////////////////////////////////////////////////////////////////////////////////
  //// SEARCH MANAGER /////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  void IGFD::SearchManager::Clear()
  {
    puSearchTag.clear();
    IGFD::Utils::ResetBuffer(puSearchBuffer);
  }

  void IGFD::SearchManager::DrawSearchBar(FileDialogInternal& vFileDialogInternal)
  {
    // search field
    if (IMGUI_BUTTON(resetButtonString "##BtnImGuiFileDialogSearchField"))
    {
      Clear();
      vFileDialogInternal.puFileManager.ApplyFilteringOnFileList(vFileDialogInternal);
    }
    if (ImGui::IsItemHovered())
      ImGui::SetTooltip("%s",FileDialog::Instance()->buttonResetSearchString);
    ImGui::SameLine();
    ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
    bool edited = ImGui::InputTextWithHint("##InputImGuiFileDialogSearchField", FileDialog::Instance()->searchString, puSearchBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
    if (ImGui::GetItemID() == ImGui::GetActiveID())
      puSearchInputIsActive = true;
    ImGui::PopItemWidth();
    if (edited)
    {
      puSearchTag = puSearchBuffer;
      vFileDialogInternal.puFileManager.ApplyFilteringOnFileList(vFileDialogInternal);
    }
  }

  /////////////////////////////////////////////////////////////////////////////////////
  //// FILTER INFOS ///////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  void IGFD::FilterManager::FilterInfos::clear()
  {
    filter.clear(); 
    collectionfilters.clear();
  }

  bool IGFD::FilterManager::FilterInfos::empty() const
  {
    return filter.empty() && collectionfilters.empty();
  }

  bool IGFD::FilterManager::FilterInfos::exist(const std::string& vFilter) const
  {
    return filter == vFilter || (collectionfilters.find(vFilter) != collectionfilters.end());
  }

  /////////////////////////////////////////////////////////////////////////////////////
  //// FILTER MANAGER /////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  void IGFD::FilterManager::ParseFilters(const char* vFilters)
  {
    prParsedFilters.clear();

    if (vFilters)
      puDLGFilters = vFilters;      // file mode
    else
      puDLGFilters.clear();        // directory mode

    if (!puDLGFilters.empty())
    {
      // ".*,.cpp,.h,.hpp"
      // "Source files{.cpp,.h,.hpp},Image files{.png,.gif,.jpg,.jpeg},.md"

      bool currentFilterFound = false;

      size_t nan = std::string::npos;
      size_t p = 0, lp = 0;
      while ((p = puDLGFilters.find_first_of("{,", p)) != nan)
      {
        FilterInfos infos;

        if (puDLGFilters[p] == '{') // {
        {
          infos.filter = puDLGFilters.substr(lp, p - lp);
          p++;
          lp = puDLGFilters.find('}', p);
          if (lp != nan)
          {
            std::string fs = puDLGFilters.substr(p, lp - p);
            auto arr = IGFD::Utils::SplitStringToVector(fs, ',', false);
            for (auto a : arr)
            {
              infos.firstFilter=a;
              infos.collectionfilters.emplace(a);
            }
          }
          p = lp + 1;
        }
        else // ,
        {
          infos.filter = puDLGFilters.substr(lp, p - lp);
          p++;
        }

        if (!currentFilterFound && prSelectedFilter.filter == infos.filter)
        {
          currentFilterFound = true;
          prSelectedFilter = infos;
        }

        lp = p;
        if (!infos.empty())
          prParsedFilters.emplace_back(infos);
      }

      std::string token = puDLGFilters.substr(lp);
      if (!token.empty())
      {
        FilterInfos infos;
        infos.filter = std::move(token);
        prParsedFilters.emplace_back(infos);
      }

      if (!currentFilterFound)
        if (!prParsedFilters.empty())
          prSelectedFilter = *prParsedFilters.begin();
    }
  }

  void IGFD::FilterManager::SetSelectedFilterWithExt(const std::string& vFilter)
  {
    if (!prParsedFilters.empty())
    {
      if (!vFilter.empty())
      {
        // std::map<std::string, FilterInfos>
        for (const auto& infos : prParsedFilters)
        {
          if (vFilter == infos.filter)
          {
            prSelectedFilter = infos;
          }
          else
          {
            // maybe this ext is in an extention so we will 
            // explore the collections is they are existing
            for (const auto& filter : infos.collectionfilters)
            {
              if (vFilter == filter)
              {
                prSelectedFilter = infos;
              }
            }
          }
        }
      }

      if (prSelectedFilter.empty())
        prSelectedFilter = *prParsedFilters.begin();
    }
  }

  void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos)
  {
    std::string _criteria;
    if (vCriteria)
      _criteria = std::string(vCriteria);
    prFilesStyle[vFlags][_criteria] = std::make_shared<FileStyle>(vInfos);
    prFilesStyle[vFlags][_criteria]->flags = vFlags;
  }

  // will be called internally 
  // will not been exposed to IGFD API
  bool IGFD::FilterManager::prFillFileStyle(FileInfos& vFileInfos) const
  {
    if (!prFilesStyle.empty())
    {
      for (const auto& _flag : prFilesStyle)
      {
        for (const auto& _file : _flag.second)
        {
          if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
          {
            if (_file.first.empty()) // for all dirs
            {
              vFileInfos.fileStyle = _file.second;
            }
            else if (_file.first == vFileInfos.fileNameExt) // for dirs who are equal to style criteria
            {
              vFileInfos.fileStyle = _file.second;
            }
          }
          else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
          {
            if (_file.first.empty()) // for all files
            {
              vFileInfos.fileStyle = _file.second;
            }
            else if (_file.first == vFileInfos.fileNameExt) // for files who are equal to style criteria
            {
              vFileInfos.fileStyle = _file.second;
            }
          }
          else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
          {
            if (_file.first.empty()) // for all links
            {
              vFileInfos.fileStyle = _file.second;
            }
            else if (_file.first == vFileInfos.fileNameExt) // for links who are equal to style criteria
            {
              vFileInfos.fileStyle = _file.second;
            }
          }

          if (_flag.first & IGFD_FileStyleByExtention)
          {
            if (_file.first == vFileInfos.fileExt)
            {
              vFileInfos.fileStyle = _file.second;
            }

            // can make sense for some dirs like the hidden by ex ".git"
            if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
            {
              if (_file.first == vFileInfos.fileExt)
              {
                vFileInfos.fileStyle = _file.second;
              }
            }
            else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
            {
              if (_file.first == vFileInfos.fileExt)
              {
                vFileInfos.fileStyle = _file.second;
              }
            }
            else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
            {
              if (_file.first == vFileInfos.fileExt)
              {
                vFileInfos.fileStyle = _file.second;
              }
            }
          }
          if (_flag.first & IGFD_FileStyleByFullName)
          {
            if (_file.first == vFileInfos.fileNameExt)
            {
              vFileInfos.fileStyle = _file.second;
            }

            if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
            {
              if (_file.first == vFileInfos.fileNameExt)
              {
                vFileInfos.fileStyle = _file.second;
              }
            }
            else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
            {
              if (_file.first == vFileInfos.fileNameExt)
              {
                vFileInfos.fileStyle = _file.second;
              }
            }
            else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
            {
              if (_file.first == vFileInfos.fileNameExt)
              {
                vFileInfos.fileStyle = _file.second;
              }
            }
          }
          if (_flag.first & IGFD_FileStyleByContainedInFullName)
          {
            if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
            {
              vFileInfos.fileStyle = _file.second;
            }

            if (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos.fileType == 'd')
            {
              if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
              {
                vFileInfos.fileStyle = _file.second;
              }
            }
            else if (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos.fileType == 'f')
            {
              if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
              {
                vFileInfos.fileStyle = _file.second;
              }
            }
            else if (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos.fileType == 'l')
            {
              if (vFileInfos.fileNameExt.find(_file.first) != std::string::npos)
              {
                vFileInfos.fileStyle = _file.second;
              }
            }
          }

          if (vFileInfos.fileStyle.use_count())
            return true;
        }
      }
    }

    return false;
  }

  void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont)
  {
    std::string _criteria;
    if (vCriteria)
      _criteria = std::string(vCriteria);
    prFilesStyle[vFlags][_criteria] = std::make_shared<FileStyle>(vColor, vIcon, vFont);
    prFilesStyle[vFlags][_criteria]->flags = vFlags;
  }

  // todo : to refactor this fucking function
  bool IGFD::FilterManager::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont **vOutFont)
  {
    if (vOutColor)
    {
      if (!prFilesStyle.empty())
      {
        if (prFilesStyle.find(vFlags) != prFilesStyle.end()) // found
        {
          if (vFlags & IGFD_FileStyleByContainedInFullName)
          {
            // search for vCriteria who are containing the criteria
            for (const auto& _file : prFilesStyle.at(vFlags))
            {
              if (vCriteria.find(_file.first) != std::string::npos)
              {
                if (_file.second.use_count())
                {
                  *vOutColor = _file.second->color;
                  if (vOutIcon)
                    *vOutIcon = _file.second->icon;
                  if (vOutFont)
                    *vOutFont = _file.second->font;
                  return true;
                }
              }
            }
          }
          else
          {
            if (prFilesStyle.at(vFlags).find(vCriteria) != prFilesStyle.at(vFlags).end()) // found
            {
              *vOutColor = prFilesStyle[vFlags][vCriteria]->color;
              if (vOutIcon)
                *vOutIcon = prFilesStyle[vFlags][vCriteria]->icon;
              if (vOutFont)
                *vOutFont = prFilesStyle[vFlags][vCriteria]->font;
              return true;
            }
          }
        }
        else
        {
          // search for flag composition
          for (const auto& _flag : prFilesStyle)
          {
            if (_flag.first & vFlags)
            {
              if (_flag.first & IGFD_FileStyleByContainedInFullName)
              {
                // search for vCriteria who are containing the criteria
                for (const auto& _file : prFilesStyle.at(_flag.first))
                {
                  if (vCriteria.find(_file.first) != std::string::npos)
                  {
                    if (_file.second.use_count())
                    {
                      *vOutColor = _file.second->color;
                      if (vOutIcon)
                        *vOutIcon = _file.second->icon;
                      if (vOutFont)
                        *vOutFont = _file.second->font;
                      return true;
                    }
                  }
                }
              }
              else
              {
                if (prFilesStyle.at(_flag.first).find(vCriteria) != prFilesStyle.at(_flag.first).end()) // found
                {
                  *vOutColor = prFilesStyle[_flag.first][vCriteria]->color;
                  if (vOutIcon)
                    *vOutIcon = prFilesStyle[_flag.first][vCriteria]->icon;
                  if (vOutFont)
                    *vOutFont = prFilesStyle[_flag.first][vCriteria]->font;
                  return true;
                }
              }
            }
          }
        }
      }
    }
    return false;
  }

  void IGFD::FilterManager::ClearFilesStyle()
  {
    prFilesStyle.clear();
  }
    
  bool IGFD::FilterManager::IsCoveredByFilters(const std::string& vTag) const
  {
    if (!puDLGFilters.empty() && !prSelectedFilter.empty())
    {
      // check if current file extention is covered by current filter
      // we do that here, for avoid doing that during filelist display
      // for better fps
      if (prSelectedFilter.exist(vTag) || prSelectedFilter.firstFilter == ".*")
      {
        return true;
      }
    }

    return false;
  }

  bool IGFD::FilterManager::DrawFilterComboBox(FileDialogInternal& vFileDialogInternal)
  {
    // combobox of filters
    if (!puDLGFilters.empty())
    {
      ImGui::SameLine();

      bool needToApllyNewFilter = false;

      ImGui::PushItemWidth(FILTER_COMBO_WIDTH*FileDialog::Instance()->DpiScale);
      if (ImGui::BeginCombo("##Filters", prSelectedFilter.filter.c_str(), ImGuiComboFlags_None))
      {
        intptr_t i = 0;
        for (const auto& filter : prParsedFilters)
        {
          const bool item_selected = (filter.filter == prSelectedFilter.filter);
          ImGui::PushID((void*)(intptr_t)i++);
          if (ImGui::Selectable(filter.filter.c_str(), item_selected))
          {
            prSelectedFilter = filter;
            needToApllyNewFilter = true;
          }
          ImGui::PopID();
        }

        ImGui::EndCombo();
      }
      ImGui::PopItemWidth();

      if (needToApllyNewFilter)
      {
        vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal);
      }

      return needToApllyNewFilter;
    }

    return false;
  }

  IGFD::FilterManager::FilterInfos IGFD::FilterManager::GetSelectedFilter()
  {
    return prSelectedFilter;
  }

  std::string IGFD::FilterManager::ReplaceExtentionWithCurrentFilter(const std::string& vFile) const
  {
    auto result = vFile;

    if (!result.empty())
    {
      // if not a collection we can replace the filter by the extention we want
      if (prSelectedFilter.collectionfilters.empty())
      {
        size_t lastPoint = vFile.find_last_of('.');
        if (lastPoint != std::string::npos)
        {
          result = result.substr(0, lastPoint);
        }

        result += prSelectedFilter.filter;
      }
    }

    return result;
  }
  
  void IGFD::FilterManager::SetDefaultFilterIfNotDefined()
  {
    if (prSelectedFilter.empty() && // no filter selected
      !prParsedFilters.empty()) // filter exist
      prSelectedFilter = *prParsedFilters.begin(); // we take the first filter
  }

  /////////////////////////////////////////////////////////////////////////////////////
  //// FILE MANAGER ///////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  IGFD::FileManager::FileManager()
  {
    puFsRoot = std::string(1u, PATH_SEP);
    puSortingDirection[0]=true;
                for (int i=1; i<4; i++) {
                  puSortingDirection[i]=false;
                }
    invalidFile.isValid=false;
  }

  void IGFD::FileManager::OpenCurrentPath(const FileDialogInternal& vFileDialogInternal)
  {
    puShowDrives = false;
    ClearComposer();
    ClearFileLists();
    if (puDLGDirectoryMode) // directory mode
      SetDefaultFileName(".");
    else
      SetDefaultFileName("");
    logV("IGFD: OpenCurrentPath()");
    ScanDir(vFileDialogInternal, GetCurrentPath());
  }

  void IGFD::FileManager::SortFields(const FileDialogInternal& vFileDialogInternal, const SortingFieldEnum& vSortingField, const bool vCanChangeOrder)
  {
    logV("IGFD: SortFields()");
    if (vSortingField != SortingFieldEnum::FIELD_NONE)
    {
      puHeaderFileName = FileDialog::Instance()->tableHeaderFileNameString;
      puHeaderFileType = FileDialog::Instance()->tableHeaderFileTypeString;
      puHeaderFileSize = FileDialog::Instance()->tableHeaderFileSizeString;
      puHeaderFileDate = FileDialog::Instance()->tableHeaderFileDateString;
#ifdef USE_THUMBNAILS
      puHeaderFileThumbnails = tableHeaderFileThumbnailsString;
#endif // #ifdef USE_THUMBNAILS
    } else {
      logV("IGFD: sorting by NONE!");
    }

    if (prFileList.empty()) {
      logV("IGFD: with an empty file list?");
    }

    if (vSortingField == SortingFieldEnum::FIELD_FILENAME)
    {
      logV("IGFD: sorting by name");
      if (vCanChangeOrder && puSortingField == vSortingField) {
        //printf("Change the sorting\n");
        puSortingDirection[0] = !puSortingDirection[0];
      }

      if (puSortingDirection[0])
      {
#ifdef USE_CUSTOM_SORTING_ICON
        puHeaderFileName = tableHeaderDescendingIcon + puHeaderFileName;
#endif // USE_CUSTOM_SORTING_ICON
        std::sort(prFileList.begin(), prFileList.end(),
          [](const FileInfos& a, const FileInfos& b) -> bool
          {
            // this code fail in c:\\Users with the link "All users". got a invalid comparator
            /*
            // use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571
            // strict ordering for file/directory types beginning in '.'
            // common on Linux platforms
            if (a.fileNameExt[0] == '.' && b.fileNameExt[0] != '.')
              return false;
            if (a.fileNameExt[0] != '.' && b.fileNameExt[0] == '.')
              return true;
            if (a.fileNameExt[0] == '.' && b.fileNameExt[0] == '.')
            {
              return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) < 0); // sort in insensitive case
            }
            */
            if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
            return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) < 0); // sort in insensitive case
          });
      }
      else
      {
#ifdef USE_CUSTOM_SORTING_ICON
        puHeaderFileName = tableHeaderAscendingIcon + puHeaderFileName;
#endif // USE_CUSTOM_SORTING_ICON
        std::sort(prFileList.begin(), prFileList.end(),
          [](const FileInfos& a, const FileInfos& b) -> bool
          {
            // this code fail in c:\\Users with the link "All users". got a invalid comparator
            /*
            // use code from https://github.com/jackm97/ImGuiFileDialog/commit/bf40515f5a1de3043e60562dc1a494ee7ecd3571
            // strict ordering for file/directory types beginning in '.'
            // common on Linux platforms
            if (a.fileNameExt[0] == '.' && b.fileNameExt[0] != '.')
              return false;
            if (a.fileNameExt[0] != '.' && b.fileNameExt[0] == '.')
              return true;
            if (a.fileNameExt[0] == '.' && b.fileNameExt[0] == '.')
            {
              return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) > 0); // sort in insensitive case
            }
            */
            return (stricmp(a.fileNameExt.c_str(), b.fileNameExt.c_str()) > 0); // sort in insensitive case
          });
      }
    }
    else if (vSortingField == SortingFieldEnum::FIELD_TYPE)
    {
      logV("IGFD: sorting by type");
      if (vCanChangeOrder && puSortingField == vSortingField)
        puSortingDirection[1] = !puSortingDirection[1];

      if (puSortingDirection[1])
      {
#ifdef USE_CUSTOM_SORTING_ICON
        puHeaderFileType = tableHeaderDescendingIcon + puHeaderFileType;
#endif // USE_CUSTOM_SORTING_ICON
        std::sort(prFileList.begin(), prFileList.end(),
          [](const FileInfos& a, const FileInfos& b) -> bool
          {
            if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
            return (a.fileExt < b.fileExt); // else
          });
      }
      else
      {
#ifdef USE_CUSTOM_SORTING_ICON
        puHeaderFileType = tableHeaderAscendingIcon + puHeaderFileType;
#endif // USE_CUSTOM_SORTING_ICON
        std::sort(prFileList.begin(), prFileList.end(),
          [](const FileInfos& a, const FileInfos& b) -> bool
          {
            if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
            return (a.fileExt > b.fileExt); // else
          });
      }
    }
    else if (vSortingField == SortingFieldEnum::FIELD_SIZE)
    {
      logV("IGFD: sorting by size");
      if (vCanChangeOrder && puSortingField == vSortingField)
        puSortingDirection[2] = !puSortingDirection[2];

      if (puSortingDirection[2])
      {
#ifdef USE_CUSTOM_SORTING_ICON
        puHeaderFileSize = tableHeaderDescendingIcon + puHeaderFileSize;
#endif // USE_CUSTOM_SORTING_ICON
        std::sort(prFileList.begin(), prFileList.end(),
          [](const FileInfos& a, const FileInfos& b) -> bool
          {
            if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
            return (a.fileSize < b.fileSize); // else
          });
      }
      else
      {
#ifdef USE_CUSTOM_SORTING_ICON
        puHeaderFileSize = tableHeaderAscendingIcon + puHeaderFileSize;
#endif // USE_CUSTOM_SORTING_ICON
        std::sort(prFileList.begin(), prFileList.end(),
          [](const FileInfos& a, const FileInfos& b) -> bool
          {
            if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
            return (a.fileSize > b.fileSize); // else
          });
      }
    }
    else if (vSortingField == SortingFieldEnum::FIELD_DATE)
    {
      logV("IGFD: sorting by date");
      if (vCanChangeOrder && puSortingField == vSortingField)
        puSortingDirection[3] = !puSortingDirection[3];

      if (puSortingDirection[3])
      {
#ifdef USE_CUSTOM_SORTING_ICON
        puHeaderFileDate = tableHeaderDescendingIcon + puHeaderFileDate;
#endif // USE_CUSTOM_SORTING_ICON
        std::sort(prFileList.begin(), prFileList.end(),
          [](const FileInfos& a, const FileInfos& b) -> bool
          {
            if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
            return (a.fileModifDate < b.fileModifDate); // else
          });
      }
      else
      {
#ifdef USE_CUSTOM_SORTING_ICON
        puHeaderFileDate = tableHeaderAscendingIcon + puHeaderFileDate;
#endif // USE_CUSTOM_SORTING_ICON
        std::sort(prFileList.begin(), prFileList.end(),
          [](const FileInfos& a, const FileInfos& b) -> bool
          {
            if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
            return (a.fileModifDate > b.fileModifDate); // else
          });
      }
    }
#ifdef USE_THUMBNAILS
    else if (vSortingField == SortingFieldEnum::FIELD_THUMBNAILS)
    {
      if (vCanChangeOrder && puSortingField == vSortingField)
        puSortingDirection[4] = !puSortingDirection[4];

      // we will compare thumbnails by :
      // 1) width 
      // 2) height

      if (puSortingDirection[4])
      {
#ifdef USE_CUSTOM_SORTING_ICON
        puHeaderFileThumbnails = tableHeaderDescendingIcon + puHeaderFileThumbnails;
#endif // USE_CUSTOM_SORTING_ICON
        std::sort(prFileList.begin(), prFileList.end(),
          [](const FileInfos& a, const FileInfos& b) -> bool
          {
            if (a.fileType != b.fileType) return (a.fileType == 'd'); // directory in first
            if (a.thumbnailInfo.textureWidth == b.thumbnailInfo.textureWidth)
              return (a.thumbnailInfo.textureHeight < b.thumbnailInfo.textureHeight);
            return (a.thumbnailInfo.textureWidth < b.thumbnailInfo.textureWidth);
          });
      }

      else
      {
#ifdef USE_CUSTOM_SORTING_ICON
        puHeaderFileThumbnails = tableHeaderAscendingIcon + puHeaderFileThumbnails;
#endif // USE_CUSTOM_SORTING_ICON
        std::sort(prFileList.begin(), prFileList.end(),
          [](const FileInfos& a, const FileInfos& b) -> bool
          {
            if (a.fileType != b.fileType) return (a.fileType != 'd'); // directory in last
            if (a.thumbnailInfo.textureWidth == b.thumbnailInfo.textureWidth)
              return (a.thumbnailInfo.textureHeight > b.thumbnailInfo.textureHeight);
            return (a.thumbnailInfo.textureWidth > b.thumbnailInfo.textureWidth);
          });
      }
    }
#endif // USE_THUMBNAILS

    if (vSortingField != SortingFieldEnum::FIELD_NONE)
    {
      puSortingField = vSortingField;
    }

    logV("IGFD: applying filtering on file list");

    ApplyFilteringOnFileList(vFileDialogInternal);
  }

  void IGFD::FileManager::ClearFileLists()
  {
    prFilteredFileList.clear();
    prFileList.clear();
  }

  std::string IGFD::FileManager::prOptimizeFilenameForSearchOperations(const std::string& vFileNameExt)
  {
    auto fileNameExt = vFileNameExt;
    // convert to lower case
    for (char& c : fileNameExt)
      c = (char)std::tolower(c);
    return fileNameExt;
  }

  void IGFD::FileManager::AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, char vFileType, void* ent)
  {
    FileInfos infos;

#ifdef _WIN32
    struct dirent* dent=(struct dirent*)ent;
#endif

    infos.filePath = vPath;
    infos.fileNameExt = vFileName;
    infos.fileNameExt_optimized = prOptimizeFilenameForSearchOperations(infos.fileNameExt);
    infos.fileType = vFileType;

    if (infos.fileNameExt.empty() || ((infos.fileNameExt == "." || infos.fileNameExt == "..") && !vFileDialogInternal.puFilterManager.puDLGFilters.empty())) return; // filename empty or filename is the current dir '.' //-V807
    if (infos.fileNameExt != ".." && (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DontShowHiddenFiles) && infos.fileNameExt[0] == '.') // dont show hidden files
      if (!vFileDialogInternal.puFilterManager.puDLGFilters.empty() || (vFileDialogInternal.puFilterManager.puDLGFilters.empty() && infos.fileNameExt != ".")) // except "." if in directory mode //-V728
        return;

    if (infos.fileType == 'f' ||
      infos.fileType == 'l') // link can have the same extention of a file
    {
      size_t lpt = infos.fileNameExt.find_last_of('.');
      if (lpt != std::string::npos)
      {
        infos.fileExt = infos.fileNameExt.substr(lpt);
      }

      for (char& i: infos.fileExt) {
        if (i>='A' && i<='Z') i+='a'-'A';
      }

      if (!vFileDialogInternal.puFilterManager.IsCoveredByFilters(infos.fileExt))
      {
        return;
      }
    }

#ifdef _WIN32
    SYSTEMTIME systemTime;
    SYSTEMTIME localTime;
    char timebuf[100];

    infos.fileSize=dent->dwin_size;
    if (FileTimeToSystemTime(&dent->dwin_mtime,&systemTime)==TRUE) {
      if (SystemTimeToTzSpecificLocalTime(NULL,&systemTime,&localTime)==TRUE) {
        snprintf(timebuf,99,"%d/%.2d/%.2d %.2d:%.2d",localTime.wYear,localTime.wMonth,localTime.wDay,localTime.wHour,localTime.wMinute);
      } else {
        snprintf(timebuf,99,"%d/%.2d/%.2d %.2d:%.2d",systemTime.wYear,systemTime.wMonth,systemTime.wDay,systemTime.wHour,systemTime.wMinute);
      }
      infos.fileModifDate=timebuf;
    } else {
      infos.fileModifDate="???";
    }
#endif

    vFileDialogInternal.puFilterManager.prFillFileStyle(infos);

    prCompleteFileInfos(infos);
    prFileList.push_back(infos);
  }

  void IGFD::FileManager::ScanDir(const FileDialogInternal& vFileDialogInternal, const std::string& vPath)
  {
    std::string  path = vPath;
    logV("IGFD: ScanDir(%s)",vPath);

    if (prCurrentPathDecomposition.empty())
    {
      logV("IGFD: the current path decomposition is empty. setting.");
      SetCurrentDir(path);
    }

    if (!prCurrentPathDecomposition.empty())
    {
      logV("IGFD: the current path decomposition is not empty. trying.");
#ifdef WIN32
      if (path == puFsRoot)
        path += std::string(1u, PATH_SEP);
#endif // WIN32

      ClearFileLists();

      struct dirent** files = nullptr;
      int n = scandir(path.c_str(), &files, nullptr, inAlphaSort);
      logV("IGFD: %d entries in directory",n);
      if (n>0)
      {
        int i;

        for (i = 0; i < n; i++)
        {
          struct dirent* ent = files[i];
          std::string where = path + std::string(PATH_SEP_STR) + std::string(ent->d_name);
          char fileType = 'f';
#if defined(HAVE_DIRENT_TYPE) || defined(_WIN32)
          if (ent->d_type != DT_UNKNOWN)
          {
            switch (ent->d_type)
            {
            case DT_REG:
              fileType = 'f'; break;
            case DT_DIR:
              fileType = 'd'; break;
            case DT_LNK: {
#ifdef _WIN32
              fileType = 'f';
#else
              DIR* dirTest = opendir(where.c_str());
              if (dirTest == NULL)
              {
                if (errno == ENOTDIR)
                {
                  fileType = 'f';
                }
                else
                {
                  // why does 'l' make it crash?
                  fileType = 'f';
                }
              }
              else
              {
                fileType = 'd';
                closedir(dirTest);
              }
#endif
              break;
            }
            default:
              fileType = 'f'; break;
            }
          }
          else
#endif // HAVE_DIRENT_TYPE
          {
#ifndef _WIN32
            struct stat filestat;
            if (stat(where.c_str(), &filestat) == 0)
            {
              if (S_ISDIR(filestat.st_mode))
              {
                fileType = 'd';
              }
              else
              {
                fileType = 'f';
              }
            }
#endif
          }

          auto fileNameExt = ent->d_name;

          AddFile(vFileDialogInternal, path, fileNameExt, fileType, ent);
        }

        for (i = 0; i < n; i++)
        {
          free(files[i]);
        }

        free(files);
      } else {
        logV("IGFD: it's empty");
      }

      logV("IGFD: sorting fields...");
      SortFields(vFileDialogInternal, puSortingField, false);
    } else {
      logE("IGFD: current path decomposition is empty!");
    }

    fileListActuallyEmpty=prFileList.empty();
  }

  bool IGFD::FileManager::GetDrives()
  {
    auto drives = IGFD::Utils::GetDrivesList();
    if (!drives.empty())
    {
      prCurrentPath.clear();
      prCurrentPathDecomposition.clear();
      ClearFileLists();
      for (auto& drive : drives)
      {
        FileInfos info;
        info.fileNameExt = drive;
        info.fileNameExt_optimized = prOptimizeFilenameForSearchOperations(drive);
        info.fileType = 'd';

        if (!info.fileNameExt.empty())
        {
          prFileList.push_back(info);
        }
      }
      puShowDrives = true;
      return true;
    }
    return false;
  }

  bool IGFD::FileManager::IsComposerEmpty()
  {
    return prCurrentPathDecomposition.empty();
  }
  
  size_t IGFD::FileManager::GetComposerSize()
  {
    return prCurrentPathDecomposition.size();
  }

  bool IGFD::FileManager::IsFileListEmpty()
  {
    return prFileList.empty();
  }

  size_t IGFD::FileManager::GetFullFileListSize()
  {
    return prFileList.size();
  }

  const FileInfos& IGFD::FileManager::GetFullFileAt(size_t vIdx)
  {
    if (vIdx < prFileList.size())
      return prFileList[vIdx];
    return invalidFile;
  }

  bool IGFD::FileManager::IsFilteredListEmpty()
  {
    return prFilteredFileList.empty();
  }

  size_t IGFD::FileManager::GetFilteredListSize()
  {
    return prFilteredFileList.size();
  }

  const FileInfos& IGFD::FileManager::GetFilteredFileAt(size_t vIdx)
  {
    if (vIdx < prFilteredFileList.size())
      return prFilteredFileList[vIdx];
    return invalidFile;
  }

  bool IGFD::FileManager::IsFileNameSelected(const std::string& vFileName)
  {
    return prSelectedFileNames.find(vFileName) != prSelectedFileNames.end();
  }

  std::string IGFD::FileManager::GetBack()
  {
    return prCurrentPathDecomposition.back();
  }

  void IGFD::FileManager::ClearComposer()
  {
    prCurrentPathDecomposition.clear();
  }

  void IGFD::FileManager::ClearAll()
  {
    ClearComposer();
    ClearFileLists();
  }

  void IGFD::FileManager::ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal)
  {
    prFilteredFileList.clear();
    for (const FileInfos& file : prFileList)
    {
      bool show = true;
      if (!file.IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag))  // if search tag
        show = false;
      if (puDLGDirectoryMode && file.fileType != 'd') // directory mode
        show = false;
      if (show)
        prFilteredFileList.push_back(file);
    }
  }

  std::string IGFD::FileManager::prRoundNumber(double vvalue, int n)
  {
    std::stringstream tmp;
    tmp << std::setprecision(n) << std::fixed << vvalue;
    return tmp.str();
  }

  std::string IGFD::FileManager::prFormatFileSize(size_t vByteSize)
  {
    if (vByteSize != 0)
    {
      static double lo = 1024.0;
      static double ko = 1024.0 * 1024.0;
      static double mo = 1024.0 * 1024.0 * 1024.0;

      auto v = (double)vByteSize;

      if (v < lo)
        return prRoundNumber(v, 0); // octet
      else if (v < ko)
        return prRoundNumber(v / lo, 2) + "K"; // ko
      else  if (v < mo)
        return prRoundNumber(v / ko, 2) + "M"; // Mo 
      else
        return prRoundNumber(v / mo, 2) + "G"; // Go 
    }

    return "";
  }

  void IGFD::FileManager::prCompleteFileInfos(FileInfos& vInfos)
  {
    if (vInfos.fileNameExt != "." &&
      vInfos.fileNameExt != "..")
    {
      // _stat struct :
      //dev_t     st_dev;     /* ID of device containing file */
      //ino_t     st_ino;     /* inode number */
      //mode_t    st_mode;    /* protection */
      //nlink_t   st_nlink;   /* number of hard links */
      //uid_t     st_uid;     /* user ID of owner */
      //gid_t     st_gid;     /* group ID of owner */
      //dev_t     st_rdev;    /* device ID (if special file) */
      //off_t     st_size;    /* total size, in bytes */
      //blksize_t st_blksize; /* blocksize for file system I/O */
      //blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
      //time_t    st_atime;   /* time of last access - not sure out of ntfs */
      //time_t    st_mtime;   /* time of last modification - not sure out of ntfs */
      //time_t    st_ctime;   /* time of last status change - not sure out of ntfs */

#ifdef _WIN32
      if (vInfos.fileType != 'd')
      {
        vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize);
      }
#else
      std::string fpn;

      if (vInfos.fileType == 'f' || vInfos.fileType == 'l' || vInfos.fileType == 'd') // file
        fpn = vInfos.filePath + std::string(1u, PATH_SEP) + vInfos.fileNameExt;

      struct stat statInfos = {};
      char timebuf[100];
      int result = stat(fpn.c_str(), &statInfos);
      if (result!=-1)
      {
        if (vInfos.fileType != 'd')
        {
          vInfos.fileSize = (size_t)statInfos.st_size;
          vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize);
        }

        size_t len = 0;
#ifdef MSVC
        struct tm _tm;
        errno_t err = localtime_s(&_tm, &statInfos.st_mtime);
        if (!err) len = strftime(timebuf, 99, FileDialog::Instance()->DateTimeFormat, &_tm);
#else // MSVC
        struct tm* _tm = localtime(&statInfos.st_mtime);
        if (_tm) len = strftime(timebuf, 99, FileDialog::Instance()->DateTimeFormat, _tm);
#endif // MSVC
        if (len)
        {
          vInfos.fileModifDate = std::string(timebuf, len);
        }
      } else {
        vInfos.fileSize=0;
        vInfos.formatedFileSize = prFormatFileSize(vInfos.fileSize);
        vInfos.fileModifDate="???";
      }
#endif
    }
  }

  void IGFD::FileManager::prRemoveFileNameInSelection(const std::string& vFileName)
  {
    prSelectedFileNames.erase(vFileName);

    if (prSelectedFileNames.size() == 1)
    {
      snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str());
    }
    else
    {
      snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", prSelectedFileNames.size());
    }
  }
  
  void IGFD::FileManager::prAddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName)
  {
    prSelectedFileNames.emplace(vFileName);

    if (prSelectedFileNames.size() == 1)
    {
      snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str());
    }
    else
    {
      snprintf(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", prSelectedFileNames.size());
    }

    if (vSetLastSelectionFileName)
      prLastSelectedFileName = vFileName;
  }

  void IGFD::FileManager::SetCurrentDir(const std::string& vPath)
  {
    std::string path = vPath;
#ifdef WIN32
    if (puFsRoot == path)
      path += std::string(1u, PATH_SEP);
#endif // WIN32
    
    DIR* dir = opendir(path.c_str());
    if (dir == nullptr)
    {
      path = ".";
      dir = opendir(path.c_str());
    }

    if (dir != nullptr)
    {
#ifdef WIN32
      DWORD numchar = 0;
      //      numchar = GetFullPathNameA(path.c_str(), PATH_MAX, real_path, nullptr);
      std::wstring wpath = IGFD::Utils::string_to_wstring(path);
      numchar = GetFullPathNameW(wpath.c_str(), 0, nullptr, nullptr);
      std::wstring fpath(numchar, 0);
      GetFullPathNameW(wpath.c_str(), numchar, (wchar_t*)fpath.data(), nullptr);
      std::string real_path = IGFD::Utils::wstring_to_string(fpath);
      if (real_path.back() == '\0') // for fix issue we can have with std::string concatenation.. if there is a \0 at end
        real_path = real_path.substr(0, real_path.size() - 1U);
      if (!real_path.empty())
#elif defined(UNIX) // UNIX is LINUX or APPLE
      char real_path[PATH_MAX]; 
      char* numchar = realpath(path.c_str(), real_path);
      if (numchar != nullptr)
#endif // WIN32
      {
        prCurrentPath = std::move(real_path);
        if (prCurrentPath[prCurrentPath.size() - 1] == PATH_SEP)
        {
          prCurrentPath = prCurrentPath.substr(0, prCurrentPath.size() - 1);
        }
        IGFD::Utils::SetBuffer(puInputPathBuffer, MAX_PATH_BUFFER_SIZE, prCurrentPath);
        prCurrentPathDecomposition = IGFD::Utils::SplitStringToVector(prCurrentPath, PATH_SEP, false);
#ifdef UNIX // UNIX is LINUX or APPLE
        prCurrentPathDecomposition.insert(prCurrentPathDecomposition.begin(), std::string(1u, PATH_SEP));
#endif // UNIX
        if (!prCurrentPathDecomposition.empty())
        {
#ifdef WIN32
          puFsRoot = prCurrentPathDecomposition[0];
#endif // WIN32
        }
      }
      closedir(dir);
    }
  }

  bool IGFD::FileManager::CreateDir(const std::string& vPath)
  {
    bool res = false;

    if (!vPath.empty())
    {
      std::string path = prCurrentPath + std::string(1u, PATH_SEP) + vPath;

      res = IGFD::Utils::CreateDirectoryIfNotExist(path);
    }

    return res;
  }

  void IGFD::FileManager::ComposeNewPath(std::vector<std::string>::iterator vIter)
  {
    std::string res;

    while (true)
    {
      if (!res.empty())
      {
#ifdef WIN32
        res = *vIter + std::string(1u, PATH_SEP) + res;
#elif defined(UNIX) // UNIX is LINUX or APPLE
        if (*vIter == puFsRoot)
          res = *vIter + res;
        else
          res = *vIter + PATH_SEP + res;
#endif // WIN32
      }
      else
        res = *vIter;

      if (vIter == prCurrentPathDecomposition.begin())
      {
#if defined(UNIX) // UNIX is LINUX or APPLE
        if (res[0] != PATH_SEP)
          res = PATH_SEP + res;
#endif // defined(UNIX)
        break;
      }

      --vIter;
    }

    prCurrentPath = std::move(res);
  }

  bool IGFD::FileManager::SetPathOnParentDirectoryIfAny()
  {
    if (prCurrentPathDecomposition.size() > 1)
    {
      ComposeNewPath(prCurrentPathDecomposition.end() - 2);
      return true;
    }
    return false;
  }

  std::string IGFD::FileManager::GetCurrentPath()
  {
    if (prCurrentPath.empty())
      prCurrentPath = ".";
    return prCurrentPath;
  }

  void IGFD::FileManager::SetCurrentPath(const std::string& vCurrentPath)
  {
    if (vCurrentPath.empty())
      prCurrentPath = ".";
    else
      prCurrentPath = vCurrentPath;
  }

  bool IGFD::FileManager::IsFileExist(const std::string& vFile, const std::string& vFileExt)
  {
    std::ifstream docFile(vFile, std::ios::in);
    logV("IGFD: IsFileExist(%s)",vFile);
    if (docFile.is_open())
    {
      docFile.close();
      return true;
    }

    std::ifstream docFileE(vFile+vFileExt, std::ios::in);
    logV("IGFD: IsFileExist(%s)",vFile+vFileExt);
    if (docFileE.is_open())
    {
      docFileE.close();
      return true;
    }
    return false;
  }

  void IGFD::FileManager::SetDefaultFileName(const std::string& vFileName)
  {
    puDLGDefaultFileName = vFileName;
    IGFD::Utils::SetBuffer(puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFileName);
  }

  bool IGFD::FileManager::SelectDirectory(const FileInfos& vInfos)
  {
    bool pathClick = false;

    if (vInfos.fileNameExt == "..")
    {
      pathClick = SetPathOnParentDirectoryIfAny();
    }
    else
    {
      std::string newPath;

      if (puShowDrives)
      {
        newPath = vInfos.fileNameExt + std::string(1u, PATH_SEP);
      }
      else
      {
#ifdef __linux__
        if (puFsRoot == prCurrentPath)
          newPath = prCurrentPath + vInfos.fileNameExt;
        else
#endif // __linux__
          newPath = prCurrentPath + std::string(1u, PATH_SEP) + vInfos.fileNameExt;
      }

      if (IGFD::Utils::IsDirectoryExist(newPath))
      {
        if (puShowDrives)
        {
          prCurrentPath = vInfos.fileNameExt;
          puFsRoot = prCurrentPath;
        }
        else
        {
          prCurrentPath = newPath; //-V820
        }
        pathClick = true;
      }
    }

    return pathClick;
  }

  void IGFD::FileManager::SelectFileName(const FileDialogInternal& vFileDialogInternal, const FileInfos& vInfos)
  {
    if (ImGui::GetIO().KeyCtrl)
    {
      if (puDLGcountSelectionMax == 0) // infinite selection
      {
        if (prSelectedFileNames.find(vInfos.fileNameExt) == prSelectedFileNames.end()) // not found +> add
        {
          prAddFileNameInSelection(vInfos.fileNameExt, true);
        }
        else // found +> remove
        {
          prRemoveFileNameInSelection(vInfos.fileNameExt);
        }
      }
      else // selection limited by size
      {
        if (prSelectedFileNames.size() < puDLGcountSelectionMax)
        {
          if (prSelectedFileNames.find(vInfos.fileNameExt) == prSelectedFileNames.end()) // not found +> add
          {
            prAddFileNameInSelection(vInfos.fileNameExt, true);
          }
          else // found +> remove
          {
            prRemoveFileNameInSelection(vInfos.fileNameExt);
          }
        }
      }
    }
    else if (ImGui::GetIO().KeyShift)
    {
      if (puDLGcountSelectionMax != 1)
      {
        prSelectedFileNames.clear();
        // we will iterate filelist and get the last selection after the start selection
        bool startMultiSelection = false;
        std::string fileNameToSelect = vInfos.fileNameExt;
        std::string savedLastSelectedFileName; // for invert selection mode
        for (const FileInfos& file : prFileList)
        {
          bool canTake = true;
          if (!file.IsTagFound(vFileDialogInternal.puSearchManager.puSearchTag)) canTake = false;
          if (canTake) // if not filtered, we will take files who are filtered by the dialog
          {
            if (file.fileNameExt == prLastSelectedFileName)
            {
              startMultiSelection = true;
              prAddFileNameInSelection(prLastSelectedFileName, false);
            }
            else if (startMultiSelection)
            {
              if (puDLGcountSelectionMax == 0) // infinite selection
              {
                prAddFileNameInSelection(file.fileNameExt, false);
              }
              else // selection limited by size
              {
                if (prSelectedFileNames.size() < puDLGcountSelectionMax)
                {
                  prAddFileNameInSelection(file.fileNameExt, false);
                }
                else
                {
                  startMultiSelection = false;
                  if (!savedLastSelectedFileName.empty())
                    prLastSelectedFileName = savedLastSelectedFileName;
                  break;
                }
              }
            }

            if (file.fileNameExt == fileNameToSelect)
            {
              if (!startMultiSelection) // we are before the last Selected FileName, so we must inverse
              {
                savedLastSelectedFileName = prLastSelectedFileName;
                prLastSelectedFileName = fileNameToSelect;
                fileNameToSelect = savedLastSelectedFileName;
                startMultiSelection = true;
                prAddFileNameInSelection(prLastSelectedFileName, false);
              }
              else
              {
                startMultiSelection = false;
                if (!savedLastSelectedFileName.empty())
                  prLastSelectedFileName = savedLastSelectedFileName;
                break;
              }
            }
          }
        }
      }
    }
    else
    {
      prSelectedFileNames.clear();
      IGFD::Utils::ResetBuffer(puFileNameBuffer);
      prAddFileNameInSelection(vInfos.fileNameExt, true);
    }
  }

  void IGFD::FileManager::DrawDirectoryCreation(const FileDialogInternal& vFileDialogInternal)
  {
    if (vFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableCreateDirectoryButton)
      return;

    if (IMGUI_BUTTON(createDirButtonString))
    {
      if (!prCreateDirectoryMode)
      {
        prCreateDirectoryMode = true;
        IGFD::Utils::ResetBuffer(puDirectoryNameBuffer);
      }
    }
    if (ImGui::IsItemHovered())
      ImGui::SetTooltip("%s",FileDialog::Instance()->buttonCreateDirString);

    if (prCreateDirectoryMode)
    {
      ImGui::SameLine();

      ImGui::PushItemWidth(100.0f);
      ImGui::InputText("##DirectoryFileName", puDirectoryNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
      ImGui::PopItemWidth();

      ImGui::SameLine();

      if (IMGUI_BUTTON(FileDialog::Instance()->okButtonString))
      {
        std::string newDir = std::string(puDirectoryNameBuffer);
        if (CreateDir(newDir))
        {
          SetCurrentPath(prCurrentPath + std::string(1u, PATH_SEP) + newDir);
          OpenCurrentPath(vFileDialogInternal);
        }

        prCreateDirectoryMode = false;
      }

      ImGui::SameLine();

      if (IMGUI_BUTTON(FileDialog::Instance()->cancelButtonString))
      {
        prCreateDirectoryMode = false;
      }
    }
  }

  void IGFD::FileManager::DrawPathComposer(const FileDialogInternal& vFileDialogInternal)
  {
    if (IMGUI_BUTTON(homeButtonString))
    {
      SetCurrentPath(FileDialog::Instance()->homePath);
      OpenCurrentPath(vFileDialogInternal);
    }
    if (ImGui::IsItemHovered())
      ImGui::SetTooltip("%s",FileDialog::Instance()->buttonResetPathString);

    ImGui::SameLine();
    if (IMGUI_BUTTON(parentDirString))
    {
      if (SetPathOnParentDirectoryIfAny()) {
        OpenCurrentPath(vFileDialogInternal);
      }
    }
    if (ImGui::IsItemHovered())
      ImGui::SetTooltip("%s",FileDialog::Instance()->buttonParentDirString);

#ifdef WIN32
    ImGui::SameLine();

    if (IMGUI_BUTTON(drivesButtonString))
    {
      puDrivesClicked = true;
    }
    if (ImGui::IsItemHovered())
      ImGui::SetTooltip("%s",FileDialog::Instance()->buttonDriveString);
#endif // WIN32

    ImGui::SameLine();
    
    if (IMGUI_BUTTON(editPathButtonString))
    {
      puInputPathActivated = true;
    }
    if (ImGui::IsItemHovered())
      ImGui::SetTooltip("%s",FileDialog::Instance()->buttonEditPathString);

    ImGui::SameLine();

    ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);

    // show current path
    if (!prCurrentPathDecomposition.empty())
    {
      ImGui::SameLine();

      if (puInputPathActivated)
      {
        ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
        ImGui::InputText("##pathedition", puInputPathBuffer, MAX_PATH_BUFFER_SIZE);
        ImGui::PopItemWidth();
      }
      else
      {
        int _id = 0;
        for (auto itPathDecomp = prCurrentPathDecomposition.begin();
          itPathDecomp != prCurrentPathDecomposition.end(); ++itPathDecomp)
        {
          if (itPathDecomp != prCurrentPathDecomposition.begin())
            ImGui::SameLine();
          ImGui::PushID(_id++);
          bool click = IMGUI_PATH_BUTTON((*itPathDecomp).c_str());
          ImGui::PopID();
          if (click)
          {
            ComposeNewPath(itPathDecomp);
            puPathClicked = true;
            break;
          }
          // activate input for path
          if (ImGui::IsItemClicked(ImGuiMouseButton_Right))
          {
            ComposeNewPath(itPathDecomp);
            IGFD::Utils::SetBuffer(puInputPathBuffer, MAX_PATH_BUFFER_SIZE, prCurrentPath);
            puInputPathActivated = true;
            break;
          }
        }
      }
    }
  }

  std::string IGFD::FileManager::GetResultingPath()
  {
    std::string path = prCurrentPath;

    if (puDLGDirectoryMode) // if directory mode
    {
      std::string selectedDirectory = puFileNameBuffer;
      if (!selectedDirectory.empty() &&
        selectedDirectory != ".")
        path += std::string(1u, PATH_SEP) + selectedDirectory;
    }

    return path;
  }

  std::string IGFD::FileManager::GetResultingFileName(FileDialogInternal& vFileDialogInternal)
  {
    if (!puDLGDirectoryMode) // if not directory mode
    {
      return puFileNameBuffer; //vFileDialogInternal.puFilterManager.ReplaceExtentionWithCurrentFilter(std::string(puFileNameBuffer));
    }

    return ""; // directory mode
  }

  std::string IGFD::FileManager::GetResultingFilePathName(FileDialogInternal& vFileDialogInternal)
  {
    std::string result = GetResultingPath();

    std::string filename = GetResultingFileName(vFileDialogInternal);
    if (!filename.empty())
    {
#ifdef UNIX
      if (puFsRoot != result)
#endif // UNIX
        result += std::string(1u, PATH_SEP);

      result += filename;
    }

    return result;
  }

  std::map<std::string, std::string> IGFD::FileManager::GetResultingSelection()
  {
    std::map<std::string, std::string> res;

    for (auto& selectedFileName : prSelectedFileNames)
    {
      std::string result = GetResultingPath();

#ifdef UNIX
      if (puFsRoot != result)
#endif // UNIX
        result += std::string(1u, PATH_SEP);

      result += selectedFileName;

      res[selectedFileName] = result;
    }

    return res;
  }

  /////////////////////////////////////////////////////////////////////////////////////
  //// FILE DIALOG INTERNAL ///////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  void IGFD::FileDialogInternal::NewFrame()
  {
    puCanWeContinue = true;  // reset flag for possibily validate the dialog
    puIsOk = false;        // reset dialog result
    puFileManager.puDrivesClicked = false;
    puFileManager.puPathClicked = false;
    
    puNeedToExitDialog = false;

#ifdef USE_DIALOG_EXIT_WITH_KEY
    if (ImGui::IsKeyPressed(IGFD_EXIT_KEY))
    {
      // we do that here with the data's defined at the last frame
      // because escape key can quit input activation and at the end of the frame all flag will be false
      // so we will detect nothing
      if (!(puFileManager.puInputPathActivated ||
        puSearchManager.puSearchInputIsActive ||
        puFileInputIsActive ||
        puFileListViewIsActive))
      {
        puNeedToExitDialog = true; // need to quit dialog
      }
    }
    else
#endif
    {
      puSearchManager.puSearchInputIsActive = false;
      puFileInputIsActive = false;
      puFileListViewIsActive = false;
    }
  }

  void IGFD::FileDialogInternal::EndFrame()
  {
    // directory change
    if (puFileManager.puPathClicked)
    {
      puFileManager.OpenCurrentPath(*this);
    }

    if (puFileManager.puDrivesClicked)
    {
      if (puFileManager.GetDrives())
      {
        puFileManager.ApplyFilteringOnFileList(*this);
      }
    }

    if (puFileManager.puInputPathActivated)
    {
      auto gio = ImGui::GetIO();
      if (ImGui::IsKeyReleased(ImGuiKey_Enter))
      {
        puFileManager.SetCurrentPath(std::string(puFileManager.puInputPathBuffer));
        puFileManager.OpenCurrentPath(*this);
        puFileManager.puInputPathActivated = false;
      }
      if (ImGui::IsKeyReleased(ImGuiKey_Escape))
      {
        puFileManager.puInputPathActivated = false;
      }
    }
  }

  void IGFD::FileDialogInternal::ResetForNewDialog()
  {
    puFileManager.fileListActuallyEmpty=false;
  }

  /////////////////////////////////////////////////////////////////////////////////////
  //// THUMBNAIL FEATURE //////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  IGFD::ThumbnailFeature::ThumbnailFeature()
  {
#ifdef USE_THUMBNAILS
    prDisplayMode = DisplayModeEnum::FILE_LIST;
#endif
  }

  IGFD::ThumbnailFeature::~ThumbnailFeature()  = default;

  void IGFD::ThumbnailFeature::NewThumbnailFrame(FileDialogInternal& vFileDialogInternal)
  {
    (void)vFileDialogInternal;

#ifdef USE_THUMBNAILS
    prStartThumbnailFileDatasExtraction();
#endif
  }

  void IGFD::ThumbnailFeature::EndThumbnailFrame(FileDialogInternal& vFileDialogInternal)
  {
#ifdef USE_THUMBNAILS
    prClearThumbnails(vFileDialogInternal);
#endif
  }

  void IGFD::ThumbnailFeature::QuitThumbnailFrame(FileDialogInternal& vFileDialogInternal)
  {
#ifdef USE_THUMBNAILS
    prStopThumbnailFileDatasExtraction();
    prClearThumbnails(vFileDialogInternal);
#endif
  }

#ifdef USE_THUMBNAILS
  void IGFD::ThumbnailFeature::prStartThumbnailFileDatasExtraction()
  {
    const bool res = prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable();
    if (!res)
    {
      prIsWorking = true;
      prCountFiles = 0U;
      prThumbnailGenerationThread = std::shared_ptr<std::thread>(
        new std::thread(&IGFD::ThumbnailFeature::prThreadThumbnailFileDatasExtractionFunc, this),
        [this](std::thread* obj)
        {
          prIsWorking = false;
          if (obj)
            obj->join();
        });
    }
  }

  bool IGFD::ThumbnailFeature::prStopThumbnailFileDatasExtraction()
  {
    const bool res = prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable();
    if (res)
    {
      prThumbnailGenerationThread.reset();
    }

    return res;
  }
  
  void IGFD::ThumbnailFeature::prThreadThumbnailFileDatasExtractionFunc()
  {
    prCountFiles = 0U;
    prIsWorking = true;

    // infinite loop while is thread working
    while(prIsWorking)
    {
      if (!prThumbnailFileDatasToGet.empty())
      {
        std::shared_ptr<FileInfos> file = nullptr;
        prThumbnailFileDatasToGetMutex.lock();
        //get the first file in the list
        file = (*prThumbnailFileDatasToGet.begin());
        prThumbnailFileDatasToGetMutex.unlock();

        // retrieve datas of the texture file if its an image file
        if (file.use_count())
        {
          if (file->fileType == 'f') //-V522
          {
            if (file->fileExt == ".png"
              || file->fileExt == ".bmp"
              || file->fileExt == ".tga"
              || file->fileExt == ".jpg" || file->fileExt == ".jpeg"
              || file->fileExt == ".gif"
              || file->fileExt == ".psd"
              || file->fileExt == ".pic"
              || file->fileExt == ".ppm" || file->fileExt == ".pgm"
              //|| file->fileExt == ".hdr" => format float so in few times
              )
            {
              auto fpn = file->filePath + std::string(1u, PATH_SEP) + file->fileNameExt;

              int w = 0;
              int h = 0;
              int chans = 0;
              uint8_t *datas = stbi_load(fpn.c_str(), &w, &h, &chans, STBI_rgb_alpha);
              if (datas)
              {
                if (w && h)
                {
                  // resize with respect to glyph ratio
                  const float ratioX = (float)w / (float)h;
                  const float newX = DisplayMode_ThumbailsList_ImageHeight * ratioX;
                  float newY = w / ratioX;
                  if (newX < w) 
                    newY = DisplayMode_ThumbailsList_ImageHeight;

                  const auto newWidth = (int)newX;
                  const auto newHeight = (int)newY;
                  const auto newBufSize = (size_t)(newWidth * newHeight * 4U); //-V112 //-V1028
                  auto resizedData = new uint8_t[newBufSize];
                  
                  const int resizeSucceeded = stbir_resize_uint8(
                    datas, w, h, 0,
                    resizedData, newWidth, newHeight, 0,
                    4); //-V112

                  if (resizeSucceeded)
                  {
                    auto th = &file->thumbnailInfo;

                    th->textureFileDatas = resizedData;
                    th->textureWidth = newWidth;
                    th->textureHeight = newHeight;
                    th->textureChannels = 4; //-V112

                    // we set that at least, because will launch the gpu creation of the texture in the main thread
                    th->isReadyToUpload = true;

                    // need gpu loading
                    prAddThumbnailToCreate(file);
                  }
                }
                else
                {
                  printf("image loading fail : w:%i h:%i c:%i\n", w, h, 4); //-V112
                }

                stbi_image_free(datas);
              }
            }
          }

          // peu importe le resultat on vire le fichicer
          // remove form this list
          // write => thread concurency issues
          prThumbnailFileDatasToGetMutex.lock();
          prThumbnailFileDatasToGet.pop_front();
          prThumbnailFileDatasToGetMutex.unlock();
        }
      }
    }
  }

  inline void inVariadicProgressBar(float fraction, const ImVec2& size_arg, const char* fmt, ...)
  {
    va_list args;
    va_start(args, fmt);
    char TempBuffer[512];
    const int w = vsnprintf(TempBuffer, 511, fmt, args);
    va_end(args);
    if (w)
    {
      ImGui::ProgressBar(fraction, size_arg, TempBuffer);
    }
  }

  void IGFD::ThumbnailFeature::prDrawThumbnailGenerationProgress()
  {
    if (prThumbnailGenerationThread.use_count() && prThumbnailGenerationThread->joinable())
    {
      if (!prThumbnailFileDatasToGet.empty())
      {
        const auto p = (float)((double)prCountFiles / (double)prThumbnailFileDatasToGet.size()); // read => no thread concurency issues
        inVariadicProgressBar(p, ImVec2(50, 0), "%u/%u", prCountFiles, (uint32_t)prThumbnailFileDatasToGet.size()); // read => no thread concurency issues
        ImGui::SameLine();
      }
    }
  }

  void IGFD::ThumbnailFeature::prAddThumbnailToLoad(const std::shared_ptr<FileInfos>& vFileInfos)
  {
    if (vFileInfos.use_count())
    {
      if (vFileInfos->fileType == 'f')
      {
        if (vFileInfos->fileExt == ".png"
          || vFileInfos->fileExt == ".bmp"
          || vFileInfos->fileExt == ".tga"
          || vFileInfos->fileExt == ".jpg" || vFileInfos->fileExt == ".jpeg"
          || vFileInfos->fileExt == ".gif"
          || vFileInfos->fileExt == ".psd"
          || vFileInfos->fileExt == ".pic"
          || vFileInfos->fileExt == ".ppm" || vFileInfos->fileExt == ".pgm"
          //|| file->fileExt == ".hdr" => format float so in few times
          )
        {
          // write => thread concurency issues
          prThumbnailFileDatasToGetMutex.lock();
          prThumbnailFileDatasToGet.push_back(vFileInfos);
          vFileInfos->thumbnailInfo.isLoadingOrLoaded = true;
          prThumbnailFileDatasToGetMutex.unlock();
        }
      }
    }
  }
  
  void IGFD::ThumbnailFeature::prAddThumbnailToCreate(const std::shared_ptr<FileInfos>& vFileInfos)
  {
    if (vFileInfos.use_count())
    {
      // write => thread concurency issues
      prThumbnailToCreateMutex.lock();
      prThumbnailToCreate.push_back(vFileInfos);
      prThumbnailToCreateMutex.unlock();
    }
  }

  void IGFD::ThumbnailFeature::prAddThumbnailToDestroy(const IGFD_Thumbnail_Info& vIGFD_Thumbnail_Info)
  {
    // write => thread concurency issues
    prThumbnailToDestroyMutex.lock();
    prThumbnailToDestroy.push_back(vIGFD_Thumbnail_Info);
    prThumbnailToDestroyMutex.unlock();
  }

  void IGFD::ThumbnailFeature::prDrawDisplayModeToolBar()
  {
    if (IMGUI_RADIO_BUTTON(DisplayMode_FilesList_ButtonString,
      prDisplayMode == DisplayModeEnum::FILE_LIST))
      prDisplayMode = DisplayModeEnum::FILE_LIST;
    if (ImGui::IsItemHovered())  ImGui::SetTooltip(DisplayMode_FilesList_ButtonHelp);
    ImGui::SameLine();
    if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsList_ButtonString,
      prDisplayMode == DisplayModeEnum::THUMBNAILS_LIST))
      prDisplayMode = DisplayModeEnum::THUMBNAILS_LIST;
    if (ImGui::IsItemHovered())  ImGui::SetTooltip(DisplayMode_ThumbailsList_ButtonHelp);
    ImGui::SameLine();
    /* todo
    if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsGrid_ButtonString,
      prDisplayMode == DisplayModeEnum::THUMBNAILS_GRID))
      prDisplayMode = DisplayModeEnum::THUMBNAILS_GRID;
    if (ImGui::IsItemHovered())  ImGui::SetTooltip(DisplayMode_ThumbailsGrid_ButtonHelp);
    ImGui::SameLine();
    */
    prDrawThumbnailGenerationProgress();
  }

  void IGFD::ThumbnailFeature::prClearThumbnails(FileDialogInternal& vFileDialogInternal)
  {
    // directory wil be changed so the file list will be erased
    if (vFileDialogInternal.puFileManager.puPathClicked)
    {
      size_t count = vFileDialogInternal.puFileManager.GetFullFileListSize();
      for (size_t idx = 0U; idx < count; idx++)
      {
        auto file = vFileDialogInternal.puFileManager.GetFullFileAt(idx);
        if (file.use_count())
        {
          if (file->thumbnailInfo.isReadyToDisplay) //-V522
          {
            prAddThumbnailToDestroy(file->thumbnailInfo);
          }
        }
      }
    }
  }

  void IGFD::ThumbnailFeature::SetCreateThumbnailCallback(const CreateThumbnailFun& vCreateThumbnailFun)
  {
    prCreateThumbnailFun = vCreateThumbnailFun;
  }

  void IGFD::ThumbnailFeature::SetDestroyThumbnailCallback(const DestroyThumbnailFun& vCreateThumbnailFun)
  {
    prDestroyThumbnailFun = vCreateThumbnailFun;
  }

  void IGFD::ThumbnailFeature::ManageGPUThumbnails()
  {
    if (prCreateThumbnailFun)
    {
      if (!prThumbnailToCreate.empty())
      {
        for (const auto& file : prThumbnailToCreate)
        {
          if (file.use_count())
          {
            prCreateThumbnailFun(&file->thumbnailInfo);
          }
        }
        prThumbnailToCreateMutex.lock();
        prThumbnailToCreate.clear();
        prThumbnailToCreateMutex.unlock();
      }
    }
    else
    {
      printf("No Callback found for create texture\nYou need to define the callback with a call to SetCreateThumbnailCallback\n");
    }

    if (prDestroyThumbnailFun)
    {
      if (!prThumbnailToDestroy.empty())
      {
        for (auto thumbnail : prThumbnailToDestroy)
        {
          prDestroyThumbnailFun(&thumbnail);
        }
        prThumbnailToDestroyMutex.lock();
        prThumbnailToDestroy.clear();
        prThumbnailToDestroyMutex.unlock();
      }
    }
    else
    {
    printf("No Callback found for destroy texture\nYou need to define the callback with a call to SetCreateThumbnailCallback\n");
    }
  }

#endif // USE_THUMBNAILS

  /////////////////////////////////////////////////////////////////////////////////////
  //// BOOKMARK FEATURE ///////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  IGFD::BookMarkFeature::BookMarkFeature()
  {
#ifdef USE_BOOKMARK
    prBookmarkWidth = defaultBookmarkPaneWith;
#endif // USE_BOOKMARK
  }

#ifdef USE_BOOKMARK
  void IGFD::BookMarkFeature::prDrawBookmarkButton()
  {
    IMGUI_TOGGLE_BUTTON(bookmarksButtonString, &prBookmarkPaneShown);

    if (ImGui::IsItemHovered())
      ImGui::SetTooltip(bookmarksButtonHelpString);
  }
  bool IGFD::BookMarkFeature::prDrawBookmarkPane(FileDialogInternal& vFileDialogInternal, const ImVec2& vSize)
  {
    bool res = false;

    ImGui::BeginChild("##bookmarkpane", vSize);

    static int selectedBookmarkForEdition = -1;

    if (IMGUI_BUTTON(addBookmarkButtonString "##ImGuiFileDialogAddBookmark"))
    {
      if (!vFileDialogInternal.puFileManager.IsComposerEmpty())
      {
        BookmarkStruct bookmark;
        bookmark.name = vFileDialogInternal.puFileManager.GetBack();
        bookmark.path = vFileDialogInternal.puFileManager.GetCurrentPath();
        prBookmarks.push_back(bookmark);
      }
    }
    if (selectedBookmarkForEdition >= 0 &&
      selectedBookmarkForEdition < (int)prBookmarks.size())
    {
      ImGui::SameLine();
      if (IMGUI_BUTTON(removeBookmarkButtonString "##ImGuiFileDialogAddBookmark"))
      {
        prBookmarks.erase(prBookmarks.begin() + selectedBookmarkForEdition);
        if (selectedBookmarkForEdition == (int)prBookmarks.size())
          selectedBookmarkForEdition--;
      }

      if (selectedBookmarkForEdition >= 0 &&
        selectedBookmarkForEdition < (int)prBookmarks.size())
      {
        ImGui::SameLine();

        ImGui::PushItemWidth(vSize.x - ImGui::GetCursorPosX());
        if (ImGui::InputText("##ImGuiFileDialogBookmarkEdit", prBookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER))
        {
          prBookmarks[(size_t)selectedBookmarkForEdition].name = std::string(prBookmarkEditBuffer);
        }
        ImGui::PopItemWidth();
      }
    }

    ImGui::Separator();

    if (!prBookmarks.empty())
    {
      prBookmarkClipper.Begin((int)prBookmarks.size(), ImGui::GetTextLineHeightWithSpacing());
      while (prBookmarkClipper.Step())
      {
        for (int i = prBookmarkClipper.DisplayStart; i < prBookmarkClipper.DisplayEnd; i++)
        {
          if (i < 0) continue;
          const BookmarkStruct& bookmark = prBookmarks[(size_t)i];
          ImGui::PushID(i);
          if (ImGui::Selectable(bookmark.name.c_str(), selectedBookmarkForEdition == i,
            ImGuiSelectableFlags_AllowDoubleClick) |
            (selectedBookmarkForEdition == -1 &&
              bookmark.path == vFileDialogInternal.puFileManager.GetCurrentPath())) // select if path is current
          {
            selectedBookmarkForEdition = i;
            IGFD::Utils::ResetBuffer(prBookmarkEditBuffer);
            IGFD::Utils::AppendToBuffer(prBookmarkEditBuffer, MAX_FILE_DIALOG_NAME_BUFFER, bookmark.name);

            if (ImGui::IsMouseDoubleClicked(0)) // apply path
            {
              vFileDialogInternal.puFileManager.SetCurrentPath(bookmark.path);
              vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal);
              res = true;
            }
          }
          ImGui::PopID();
          if (ImGui::IsItemHovered())
            ImGui::SetTooltip("%s", bookmark.path.c_str()); //-V111
        }
      }
      prBookmarkClipper.End();
    }

    ImGui::EndChild();

    return res;
  }

  std::string IGFD::BookMarkFeature::SerializeBookmarks()
  {
    std::string res;

    size_t idx = 0;
    for (auto& it : prBookmarks)
    {
      if (idx++ != 0)
        res += "##"; // ## because reserved by imgui, so an input text cant have ##
      res += it.name + "##" + it.path;
    }

    return res;
  }

  void IGFD::BookMarkFeature::DeserializeBookmarks(const std::string& vBookmarks)
  {
    if (!vBookmarks.empty())
    {
      prBookmarks.clear();
      auto arr = IGFD::Utils::SplitStringToVector(vBookmarks, '#', false);
      for (size_t i = 0; i < arr.size(); i += 2)
      {
        BookmarkStruct bookmark;
        bookmark.name = arr[i];
        if (i + 1 < arr.size()) // for avoid crash if arr size is impair due to user mistake after edition
        {
          // if bad format we jump this bookmark
          bookmark.path = arr[i + 1];
          prBookmarks.push_back(bookmark);
        }
      }
    }
  }
#endif // USE_BOOKMARK

  /////////////////////////////////////////////////////////////////////////////////////
  //// KEY EXPLORER FEATURE ///////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  KeyExplorerFeature::KeyExplorerFeature() = default;

#ifdef USE_EXPLORATION_BY_KEYS
  bool IGFD::KeyExplorerFeature::prLocateItem_Loop(FileDialogInternal& vFileDialogInternal, ImWchar vC)
  {
    bool found = false;

    auto& fdi = vFileDialogInternal.puFileManager;
    if (!fdi.IsFilteredListEmpty())
    {
      auto countFiles = fdi.GetFilteredListSize();
      for (size_t i = prLocateFileByInputChar_lastFileIdx; i < countFiles; i++)
      {
        auto nfo = fdi.GetFilteredFileAt(i);
        if (nfo.use_count())
        {
          if (nfo->fileNameExt_optimized[0] == vC || // lower case search //-V522
            nfo->fileNameExt[0] == vC) // maybe upper case search
          {
            //float p = ((float)i) * ImGui::GetTextLineHeightWithSpacing();
            float p = (float)((double)i / (double)countFiles) * ImGui::GetScrollMaxY();
            ImGui::SetScrollY(p);
            prLocateFileByInputChar_lastFound = true;
            prLocateFileByInputChar_lastFileIdx = i;
            prStartFlashItem(prLocateFileByInputChar_lastFileIdx);

            auto infos = fdi.GetFilteredFileAt(prLocateFileByInputChar_lastFileIdx);
            if (infos.use_count())
            {
              if (infos->fileType == 'd') //-V522
              {
                if (fdi.puDLGDirectoryMode) // directory chooser
                {
                  fdi.SelectFileName(vFileDialogInternal, infos);
                }
              }
              else
              {
                fdi.SelectFileName(vFileDialogInternal, infos);
              }

              found = true;
              break;
            }
          }
        }
      }
    }

    return found;
  }

  void IGFD::KeyExplorerFeature::prLocateByInputKey(FileDialogInternal& vFileDialogInternal)
  {
    ImGuiContext& g = *GImGui;
    auto& fdi = vFileDialogInternal.puFileManager;
    if (!g.ActiveId && !fdi.IsFilteredListEmpty())
    {
      auto& queueChar = ImGui::GetIO().InputQueueCharacters;
      auto countFiles = fdi.GetFilteredListSize();

      // point by char
      if (!queueChar.empty())
      {
        ImWchar c = queueChar.back();
        if (prLocateFileByInputChar_InputQueueCharactersSize != queueChar.size())
        {
          if (c == prLocateFileByInputChar_lastChar) // next file starting with same char until
          {
            if (prLocateFileByInputChar_lastFileIdx < countFiles - 1U)
              prLocateFileByInputChar_lastFileIdx++;
            else
              prLocateFileByInputChar_lastFileIdx = 0;
          }

          if (!prLocateItem_Loop(vFileDialogInternal, c))
          {
            // not found, loop again from 0 this time
            prLocateFileByInputChar_lastFileIdx = 0;
            prLocateItem_Loop(vFileDialogInternal, c);
          }

          prLocateFileByInputChar_lastChar = c;
        }
      }

      prLocateFileByInputChar_InputQueueCharactersSize = queueChar.size();
    }
  }

  void IGFD::KeyExplorerFeature::prExploreWithkeys(FileDialogInternal& vFileDialogInternal, ImGuiID vListViewID)
  {
    auto& fdi = vFileDialogInternal.puFileManager;
    if (!fdi.IsFilteredListEmpty())
    {
      bool canWeExplore = false;
      bool hasNav = (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
      
      ImGuiContext& g = *GImGui;
      if (!hasNav && !g.ActiveId) // no nav and no activated inputs
        canWeExplore = true;

      if (g.NavId && g.NavId == vListViewID)
      {
        if (ImGui::IsKeyPressedMap(IGFD_KEY_ENTER) ||
          ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter) ||
          ImGui::IsKeyPressedMap(ImGuiKey_Space))
        {
          ImGui::ActivateItem(vListViewID);
          ImGui::SetActiveID(vListViewID, g.CurrentWindow);
        }
      }
      
      if (vListViewID == g.LastActiveId-1) // if listview id is the last acticated nav id (ImGui::ActivateItem(vListViewID);)
        canWeExplore = true;

      if (canWeExplore)
      {
        if (ImGui::IsKeyPressedMap(ImGuiKey_Escape))
        {
          ImGui::ClearActiveID();
          g.LastActiveId = 0;
        }

        auto countFiles = fdi.GetFilteredListSize();

        // explore
        bool exploreByKey = false;
        bool enterInDirectory = false;
        bool exitDirectory = false;

        if ((hasNav && ImGui::IsKeyPressedMap(ImGuiKey_UpArrow)) || (!hasNav && ImGui::IsKeyPressed(IGFD_KEY_UP)))
        {
          exploreByKey = true;
          if (prLocateFileByInputChar_lastFileIdx > 0)
            prLocateFileByInputChar_lastFileIdx--;
          else
            prLocateFileByInputChar_lastFileIdx = countFiles - 1U;
        }
        else if ((hasNav && ImGui::IsKeyPressedMap(ImGuiKey_DownArrow)) || (!hasNav && ImGui::IsKeyPressed(IGFD_KEY_DOWN)))
        {
          exploreByKey = true;
          if (prLocateFileByInputChar_lastFileIdx < countFiles - 1U)
            prLocateFileByInputChar_lastFileIdx++;
          else
            prLocateFileByInputChar_lastFileIdx = 0U;
        }
        else if (ImGui::IsKeyReleased(IGFD_KEY_ENTER))
        {
          exploreByKey = true;
          enterInDirectory = true;
        }
        else if (ImGui::IsKeyReleased(IGFD_KEY_BACKSPACE))
        {
          exploreByKey = true;
          exitDirectory = true;
        }

        if (exploreByKey)
        {
          //float totalHeight = prFilteredFileList.size() * ImGui::GetTextLineHeightWithSpacing();
          float p = (float)((double)prLocateFileByInputChar_lastFileIdx / (double)(countFiles - 1U)) * ImGui::GetScrollMaxY();// seems not udpated in tables version outside tables
          //float p = ((float)locateFileByInputChar_lastFileIdx) * ImGui::GetTextLineHeightWithSpacing();
          ImGui::SetScrollY(p);
          prStartFlashItem(prLocateFileByInputChar_lastFileIdx);

          auto infos = fdi.GetFilteredFileAt(prLocateFileByInputChar_lastFileIdx);
          if (infos.use_count())
          {
            if (infos->fileType == 'd') //-V522
            {
              if (!fdi.puDLGDirectoryMode || enterInDirectory)
              {
                if (enterInDirectory)
                {
                  if (fdi.SelectDirectory(infos))
                  {
                    // changement de repertoire
                    vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal);
                    if (prLocateFileByInputChar_lastFileIdx > countFiles - 1U)
                    {
                      prLocateFileByInputChar_lastFileIdx = 0;
                    }
                  }
                }
              }
              else // directory chooser
              {
                fdi.SelectFileName(vFileDialogInternal, infos);
              }
            }
            else
            {
              fdi.SelectFileName(vFileDialogInternal, infos);
            }

            if (exitDirectory)
            {
              auto nfo = std::make_shared<FileInfos>();
              nfo->fileNameExt = "..";

              if (fdi.SelectDirectory(nfo))
              {
                // changement de repertoire
                vFileDialogInternal.puFileManager.OpenCurrentPath(vFileDialogInternal);
                if (prLocateFileByInputChar_lastFileIdx > countFiles - 1U)
                {
                  prLocateFileByInputChar_lastFileIdx = 0;
                }
              }
#ifdef WIN32
              else
              {
                if (fdi.GetComposerSize() == 1U)
                {
                  if (fdi.GetDrives())
                  {
                    fdi.ApplyFilteringOnFileList(vFileDialogInternal);
                  }
                }
              }
#endif // WIN32
            }
          }
        }
      }
    }
  }

  bool IGFD::KeyExplorerFeature::prFlashableSelectable(const char* label, bool selected,
    ImGuiSelectableFlags flags, bool vFlashing, const ImVec2& size_arg)
  {
    using namespace ImGui;

    ImGuiWindow* window = GetCurrentWindow();
    if (window->SkipItems)
      return false;

    ImGuiContext& g = *GImGui;
    const ImGuiStyle& style = g.Style;

    // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.
    ImGuiID id = window->GetID(label);
    ImVec2 label_size = CalcTextSize(label, nullptr, true);
    ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); //-V550
    ImVec2 pos = window->DC.CursorPos;
    pos.y += window->DC.CurrLineTextBaseOffset;
    ItemSize(size, 0.0f);

    // Fill horizontal space
    // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets.
    const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0;
    const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x;
    const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;
    if (fabs(size_arg.x) < FLT_EPSILON || (flags & ImGuiSelectableFlags_SpanAvailWidth))
      size.x = ImMax(label_size.x, max_x - min_x);

    // Text stays at the submission position, but bounding box may be extended on both sides
    const ImVec2 text_min = pos;
    const ImVec2 text_max(min_x + size.x, pos.y + size.y);

    // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.
    ImRect bb(min_x, pos.y, text_max.x, text_max.y);
    if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0)
    {
      const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x;
      const float spacing_y = style.ItemSpacing.y;
      const float spacing_L = IM_FLOOR(spacing_x * 0.50f);
      const float spacing_U = IM_FLOOR(spacing_y * 0.50f);
      bb.Min.x -= spacing_L;
      bb.Min.y -= spacing_U;
      bb.Max.x += (spacing_x - spacing_L);
      bb.Max.y += (spacing_y - spacing_U);
    }
    //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }

    // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable..
    const float backup_clip_rect_min_x = window->ClipRect.Min.x;
    const float backup_clip_rect_max_x = window->ClipRect.Max.x;
    if (span_all_columns)
    {
      window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
      window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
    }

    bool item_add;
    const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;
    if (disabled_item)
    {
      ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
      g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
      item_add = ItemAdd(bb, id);
      g.CurrentItemFlags = backup_item_flags;
    }
    else
    {
      item_add = ItemAdd(bb, id);
    }

    if (span_all_columns)
    {
      window->ClipRect.Min.x = backup_clip_rect_min_x;
      window->ClipRect.Max.x = backup_clip_rect_max_x;
    }

    if (!item_add)
      return false;

    const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
    if (disabled_item && !disabled_global) // Only testing this as an optimization
      BeginDisabled(true);

    // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,
    // which would be advantageous since most selectable are not selected.
    if (span_all_columns && window->DC.CurrentColumns)
      PushColumnsBackground();
    else if (span_all_columns && g.CurrentTable)
      TablePushBackgroundChannel();

    // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
    ImGuiButtonFlags button_flags = 0;
    if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }
    if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; }
    if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
    if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
    if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; }

    const bool was_selected = selected;
    bool hovered, held;
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);

    // Auto-select when moved into
    // - This will be more fully fleshed in the range-select branch
    // - This is not exposed as it won't nicely work with some user side handling of shift/control
    // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons
    //   - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope())
    //   - (2) usage will fail with clipped items
    //   The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
    if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent)
      if (g.NavJustMovedToId == id)
        selected = pressed = true;

    // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
    if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
    {
      if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
      {
        SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos));
        g.NavDisableHighlight = true;
      }
    }
    if (pressed)
      MarkItemEdited(id);

    if (flags & ImGuiSelectableFlags_AllowItemOverlap)
      SetItemAllowOverlap();

    // In this branch, Selectable() cannot toggle the selection so this will never trigger.
    if (selected != was_selected) //-V547
      g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;

    // Render
    if ((held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) || vFlashing)
      hovered = true;
    if (hovered || selected)
    {
      const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
      RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
    }
    RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);

    if (span_all_columns && window->DC.CurrentColumns)
      PopColumnsBackground();
    else if (span_all_columns && g.CurrentTable)
      TablePopBackgroundChannel();

    RenderTextClipped(text_min, text_max, label, nullptr, &label_size, style.SelectableTextAlign, &bb);

    // Automatically close popups
    if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup))
      CloseCurrentPopup();

    if (disabled_item && !disabled_global)
      EndDisabled();

    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
    return pressed; //-V1020
  }

  void IGFD::KeyExplorerFeature::prStartFlashItem(size_t vIdx)
  {
    prFlashAlpha = 1.0f;
    prFlashedItem = vIdx;
  }

  bool IGFD::KeyExplorerFeature::prBeginFlashItem(size_t vIdx)
  {
    bool res = false;

    if (prFlashedItem == vIdx &&
      std::abs(prFlashAlpha - 0.0f) > 0.00001f)
    {
      prFlashAlpha -= prFlashAlphaAttenInSecs * ImGui::GetIO().DeltaTime;
      if (prFlashAlpha < 0.0f) prFlashAlpha = 0.0f;

      ImVec4 hov = ImGui::GetStyleColorVec4(ImGuiCol_HeaderHovered);
      hov.w = prFlashAlpha;
      ImGui::PushStyleColor(ImGuiCol_HeaderHovered, hov);
      res = true;
    }

    return res;
  }

  void IGFD::KeyExplorerFeature::prEndFlashItem()
  {
    ImGui::PopStyleColor();
  }

  void IGFD::KeyExplorerFeature::SetFlashingAttenuationInSeconds(float vAttenValue)
  {
    prFlashAlphaAttenInSecs = 1.0f / ImMax(vAttenValue, 0.01f);
  }
#endif // USE_EXPLORATION_BY_KEYS

  /////////////////////////////////////////////////////////////////////////////////////
  //// FILE DIALOG CONSTRUCTOR / DESTRUCTOR ///////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////////////

  IGFD::FileDialog::FileDialog() : BookMarkFeature(), KeyExplorerFeature(), ThumbnailFeature() {DpiScale=1.0f; singleClickSel=false; mobileMode=false;}
  IGFD::FileDialog::~FileDialog() = default;

  //////////////////////////////////////////////////////////////////////////////////////////////////
  ///// FILE DIALOG STANDARD DIALOG ////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////

  // path and fileNameExt can be specified
  void IGFD::FileDialog::OpenDialog(
    const std::string& vKey,
    const std::string& vTitle,
    const char* vFilters,
    const std::string& vPath,
    const std::string& vFileName,
    const int& vCountSelectionMax,
    UserDatas vUserDatas,
    ImGuiFileDialogFlags vFlags,
    SelectFun vSelectFun)
  {
    if (prFileDialogInternal.puShowDialog) // if already opened, quit
      return;

    prFileDialogInternal.ResetForNewDialog();

    prFileDialogInternal.puDLGkey = vKey;
    prFileDialogInternal.puDLGtitle = vTitle;
    prFileDialogInternal.puDLGuserDatas = vUserDatas;
    prFileDialogInternal.puDLGflags = vFlags;
    prFileDialogInternal.puDLGselFun = vSelectFun;
    prFileDialogInternal.puDLGoptionsPane = nullptr;
    prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f;
    prFileDialogInternal.puDLGmodal = false;

    prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear();
    prFileDialogInternal.puFilterManager.ParseFilters(vFilters);

    prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr);
    if (vPath.empty())
      prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath();
    else
      prFileDialogInternal.puFileManager.puDLGpath = vPath;
    prFileDialogInternal.puFileManager.SetCurrentPath(vPath);
    prFileDialogInternal.puFileManager.puDLGcountSelectionMax = (size_t)vCountSelectionMax;
    prFileDialogInternal.puFileManager.SetDefaultFileName(vFileName);

    prFileDialogInternal.puFileManager.ClearAll();
    
    prFileDialogInternal.puShowDialog = true;          // open dialog
  }

  // path and filename are obtained from filePathName
  void IGFD::FileDialog::OpenDialog(
    const std::string& vKey,
    const std::string& vTitle,
    const char* vFilters,
    const std::string& vFilePathName,
    const int& vCountSelectionMax,
    UserDatas vUserDatas,
    ImGuiFileDialogFlags vFlags,
    SelectFun vSelectFun)
  {
    if (prFileDialogInternal.puShowDialog) // if already opened, quit
      return;

    prFileDialogInternal.ResetForNewDialog();

    prFileDialogInternal.puDLGkey = vKey;
    prFileDialogInternal.puDLGtitle = vTitle;
    prFileDialogInternal.puDLGoptionsPane = nullptr;
    prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f;
    prFileDialogInternal.puDLGuserDatas = vUserDatas;
    prFileDialogInternal.puDLGflags = vFlags;
    prFileDialogInternal.puDLGselFun = vSelectFun;
    prFileDialogInternal.puDLGmodal = false;

    auto ps = IGFD::Utils::ParsePathFileName(vFilePathName);
    if (ps.isOk)
    {
      prFileDialogInternal.puFileManager.puDLGpath = ps.path;
      prFileDialogInternal.puFileManager.SetDefaultFileName("");
      prFileDialogInternal.puFilterManager.puDLGdefaultExt = "." + ps.ext;
    }
    else
    {
      prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath();
      prFileDialogInternal.puFileManager.SetDefaultFileName("");
      prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear();
    }

    prFileDialogInternal.puFilterManager.ParseFilters(vFilters);
    prFileDialogInternal.puFilterManager.SetSelectedFilterWithExt(
      prFileDialogInternal.puFilterManager.puDLGdefaultExt);
    
    prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath);

    prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr);
    prFileDialogInternal.puFileManager.puDLGcountSelectionMax = vCountSelectionMax; //-V101

    prFileDialogInternal.puFileManager.ClearAll();
    
    prFileDialogInternal.puShowDialog = true;
  }

  // with pane
  // path and fileNameExt can be specified
  void IGFD::FileDialog::OpenDialog(
    const std::string& vKey,
    const std::string& vTitle,
    const char* vFilters,
    const std::string& vPath,
    const std::string& vFileName,
    const PaneFun& vSidePane,
    const float& vSidePaneWidth,
    const int& vCountSelectionMax,
    UserDatas vUserDatas,
    ImGuiFileDialogFlags vFlags,
    SelectFun vSelectFun)
  {
    if (prFileDialogInternal.puShowDialog) // if already opened, quit
      return;

    prFileDialogInternal.ResetForNewDialog();

    prFileDialogInternal.puDLGkey = vKey;
    prFileDialogInternal.puDLGtitle = vTitle;
    prFileDialogInternal.puDLGuserDatas = vUserDatas;
    prFileDialogInternal.puDLGflags = vFlags;
    prFileDialogInternal.puDLGselFun = vSelectFun;
    prFileDialogInternal.puDLGoptionsPane = vSidePane;
    prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth;
    prFileDialogInternal.puDLGmodal = false;

    prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear();
    prFileDialogInternal.puFilterManager.ParseFilters(vFilters);

    prFileDialogInternal.puFileManager.puDLGcountSelectionMax = (size_t)vCountSelectionMax;
    prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr);
    if (vPath.empty())
      prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath();
    else
      prFileDialogInternal.puFileManager.puDLGpath = vPath;

    prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath);

    prFileDialogInternal.puFileManager.SetDefaultFileName(vFileName);

    prFileDialogInternal.puFileManager.ClearAll();
    
    prFileDialogInternal.puShowDialog = true;          // open dialog
  }

  // with pane
  // path and filename are obtained from filePathName
  void IGFD::FileDialog::OpenDialog(
    const std::string& vKey,
    const std::string& vTitle,
    const char* vFilters,
    const std::string& vFilePathName,
    const PaneFun& vSidePane,
    const float& vSidePaneWidth,
    const int& vCountSelectionMax,
    UserDatas vUserDatas,
    ImGuiFileDialogFlags vFlags,
    SelectFun vSelectFun)
  {
    if (prFileDialogInternal.puShowDialog) // if already opened, quit
      return;

    prFileDialogInternal.ResetForNewDialog();

    prFileDialogInternal.puDLGkey = vKey;
    prFileDialogInternal.puDLGtitle = vTitle;
    prFileDialogInternal.puDLGoptionsPane = vSidePane;
    prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth;
    prFileDialogInternal.puDLGuserDatas = vUserDatas;
    prFileDialogInternal.puDLGflags = vFlags;
    prFileDialogInternal.puDLGselFun = vSelectFun;
    prFileDialogInternal.puDLGmodal = false;

    auto ps = IGFD::Utils::ParsePathFileName(vFilePathName);
    if (ps.isOk)
    {
      prFileDialogInternal.puFileManager.puDLGpath = ps.path;
      prFileDialogInternal.puFileManager.SetDefaultFileName(vFilePathName);
      prFileDialogInternal.puFilterManager.puDLGdefaultExt = "." + ps.ext;
    }
    else
    {
      prFileDialogInternal.puFileManager.puDLGpath = prFileDialogInternal.puFileManager.GetCurrentPath();
      prFileDialogInternal.puFileManager.SetDefaultFileName("");
      prFileDialogInternal.puFilterManager.puDLGdefaultExt.clear();
    }

    prFileDialogInternal.puFileManager.SetCurrentPath(prFileDialogInternal.puFileManager.puDLGpath);

    prFileDialogInternal.puFileManager.puDLGcountSelectionMax = vCountSelectionMax; //-V101
    prFileDialogInternal.puFileManager.puDLGDirectoryMode = (vFilters == nullptr);
    prFileDialogInternal.puFilterManager.ParseFilters(vFilters);
    prFileDialogInternal.puFilterManager.SetSelectedFilterWithExt(
      prFileDialogInternal.puFilterManager.puDLGdefaultExt);

    prFileDialogInternal.puFileManager.ClearAll();

    prFileDialogInternal.puShowDialog = true;
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////
  ///// FILE DIALOG MODAL DIALOG ///////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////

  void IGFD::FileDialog::OpenModal(
    const std::string& vKey,
    const std::string& vTitle,
    const char* vFilters,
    const std::string& vPath,
    const std::string& vFileName,
    const int& vCountSelectionMax,
    UserDatas vUserDatas,
    ImGuiFileDialogFlags vFlags,
    SelectFun vSelectFun)
  {
    if (prFileDialogInternal.puShowDialog) // if already opened, quit
      return;

    OpenDialog(
      vKey, vTitle, vFilters,
      vPath, vFileName,
      vCountSelectionMax, vUserDatas, vFlags, vSelectFun);

    prFileDialogInternal.puDLGmodal = true;
  }

  void IGFD::FileDialog::OpenModal(
    const std::string& vKey,
    const std::string& vTitle,
    const char* vFilters,
    const std::string& vFilePathName,
    const int& vCountSelectionMax,
    UserDatas vUserDatas,
    ImGuiFileDialogFlags vFlags,
    SelectFun vSelectFun)
  {
    if (prFileDialogInternal.puShowDialog) // if already opened, quit
      return;

    OpenDialog(
      vKey, vTitle, vFilters,
      vFilePathName,
      vCountSelectionMax, vUserDatas, vFlags, vSelectFun);

    prFileDialogInternal.puDLGmodal = true;
  }

  // with pane
  // path and fileNameExt can be specified
  void IGFD::FileDialog::OpenModal(
    const std::string& vKey,
    const std::string& vTitle,
    const char* vFilters,
    const std::string& vPath,
    const std::string& vFileName,
    const PaneFun& vSidePane,
    const float& vSidePaneWidth,
    const int& vCountSelectionMax,
    UserDatas vUserDatas,
    ImGuiFileDialogFlags vFlags,
    SelectFun vSelectFun)
  {
    if (prFileDialogInternal.puShowDialog) // if already opened, quit
      return;

    OpenDialog(
      vKey, vTitle, vFilters,
      vPath, vFileName,
      vSidePane, vSidePaneWidth,
      vCountSelectionMax, vUserDatas, vFlags, vSelectFun);

    prFileDialogInternal.puDLGmodal = true;
  }

  // with pane
  // path and filename are obtained from filePathName
  void IGFD::FileDialog::OpenModal(
    const std::string& vKey,
    const std::string& vTitle,
    const char* vFilters,
    const std::string& vFilePathName,
    const PaneFun& vSidePane,
    const float& vSidePaneWidth,
    const int& vCountSelectionMax,
    UserDatas vUserDatas,
    ImGuiFileDialogFlags vFlags,
    SelectFun vSelectFun)
  {
    if (prFileDialogInternal.puShowDialog) // if already opened, quit
      return;

    OpenDialog(
      vKey, vTitle, vFilters,
      vFilePathName,
      vSidePane, vSidePaneWidth,
      vCountSelectionMax, vUserDatas, vFlags, vSelectFun);

    prFileDialogInternal.puDLGmodal = true;
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////
  ///// FILE DIALOG DISPLAY FUNCTION ///////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////////////

  bool IGFD::FileDialog::Display(const std::string& vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize)
  {
    bool res = false;

    if (prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey)
    {
      if (prFileDialogInternal.puUseCustomLocale)
        setlocale(prFileDialogInternal.puLocaleCategory, prFileDialogInternal.puLocaleBegin.c_str());

      auto& fdFile = prFileDialogInternal.puFileManager;
      auto& fdFilter = prFileDialogInternal.puFilterManager;

      static ImGuiWindowFlags flags;

      // to be sure than only one dialog is displayed per frame
      ImGuiContext& g = *GImGui;
      if (g.FrameCount == prFileDialogInternal.puLastImGuiFrameCount) // one instance was displayed this frame before for this key +> quit
        return res;
      prFileDialogInternal.puLastImGuiFrameCount = g.FrameCount; // mark this instance as used this frame

      std::string name = prFileDialogInternal.puDLGtitle + "##" + prFileDialogInternal.puDLGkey;
      if (prFileDialogInternal.puName != name)
      {
        fdFile.ClearComposer();
        fdFile.ClearFileLists();
        flags = vFlags;
      }

      NewFrame();

#ifdef IMGUI_HAS_VIEWPORT
      if (!ImGui::GetIO().ConfigViewportsNoDecoration)
      {
        // https://github.com/ocornut/imgui/issues/4534
        ImGuiWindowClass window_class;
        window_class.ViewportFlagsOverrideClear = ImGuiViewportFlags_NoDecoration;
        ImGui::SetNextWindowClass(&window_class);
      }
#endif // IMGUI_HAS_VIEWPORT

      ImGui::SetNextWindowSizeConstraints(vMinSize, vMaxSize);

      bool beg = false;
      if (prFileDialogInternal.puDLGmodal &&
        !prFileDialogInternal.puOkResultToConfirm) // disable modal because the confirm dialog for overwrite is a new modal
      {
        ImGui::OpenPopup(name.c_str());
        beg = ImGui::BeginPopupModal(name.c_str(), (bool*)nullptr,
          flags | ImGuiWindowFlags_NoScrollbar);
      }
      else
      {
        beg = ImGui::Begin(name.c_str(), (bool*)nullptr, flags | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking);
      }
      if (beg)
      {
        ImGui::SetWindowPos(ImVec2((ImGui::GetMainViewport()->Size.x-ImGui::GetWindowWidth())*0.5f,(ImGui::GetMainViewport()->Size.y-ImGui::GetWindowHeight())*0.5f));
        if (ImGui::GetWindowSize().x<vMinSize.x || ImGui::GetWindowSize().y<vMinSize.y) {
          ImGui::SetWindowSize(vMinSize,ImGuiCond_Always);
        }
#ifdef IMGUI_HAS_VIEWPORT
        // if decoration is enabled we disable the resizing feature of imgui for avoid crash with SDL2 and GLFW3
        if (ImGui::GetIO().ConfigViewportsNoDecoration)
        {
          flags = vFlags;
        }
        else
        {
          auto win = ImGui::GetCurrentWindowRead();
          if (win->Viewport->Idx != 0)
            flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar;
          else
            flags = vFlags;
        }
#endif // IMGUI_HAS_VIEWPORT

        prFileDialogInternal.puName = name; //-V820
        puAnyWindowsHovered |= ImGui::IsWindowHovered();

        if (fdFile.puDLGpath.empty())
          fdFile.puDLGpath = "."; // defaut path is '.'

        fdFilter.SetDefaultFilterIfNotDefined();

        // init list of files
        if (fdFile.IsFileListEmpty() && !fdFile.puShowDrives && !fdFile.fileListActuallyEmpty)
        {
          IGFD::Utils::ReplaceString(fdFile.puDLGDefaultFileName, fdFile.puDLGpath, ""); // local path
          if (!fdFile.puDLGDefaultFileName.empty())
          {
            fdFile.SetDefaultFileName(fdFile.puDLGDefaultFileName);
            fdFilter.SetSelectedFilterWithExt(fdFilter.puDLGdefaultExt);
          } else if (fdFile.puDLGDirectoryMode) { // directory mode
            fdFile.SetDefaultFileName(".");
          }
          logV("IGFD: fdFile.IsFileListEmpty() and !fdFile.puShowDrives");
          fdFile.ScanDir(prFileDialogInternal, fdFile.puDLGpath);
        }

        // draw dialog parts
        prDrawHeader(); // bookmark, directory, path
        res = prDrawContent(); // bookmark, files view, side pane
        bool res1 = prDrawFooter(); // file field, filter combobox, ok/cancel buttons
        if (!res) res=res1;

        EndFrame();

        // for display in dialog center, the confirm to overwrite dlg
        prFileDialogInternal.puDialogCenterPos = ImGui::GetCurrentWindowRead()->ContentRegionRect.GetCenter();

        // when the confirm to overwrite dialog will appear we need to 
        // disable the modal mode of the main file dialog
        // see prOkResultToConfirm under
        if (prFileDialogInternal.puDLGmodal &&
          !prFileDialogInternal.puOkResultToConfirm)
          ImGui::EndPopup();
      }

      // same things here regarding prOkResultToConfirm
      if (!prFileDialogInternal.puDLGmodal || prFileDialogInternal.puOkResultToConfirm)
        ImGui::End();

      // confirm the result and show the confirm to overwrite dialog if needed
      res =  prConfirm_Or_OpenOverWriteFileDialog_IfNeeded(res, vFlags);
      
      if (prFileDialogInternal.puUseCustomLocale)
        setlocale(prFileDialogInternal.puLocaleCategory, prFileDialogInternal.puLocaleEnd.c_str());
    }

    return res;
  }

  void IGFD::FileDialog::NewFrame()
  {
    prFileDialogInternal.NewFrame();
    NewThumbnailFrame(prFileDialogInternal);
  }
  
  void IGFD::FileDialog::EndFrame()
  {
    EndThumbnailFrame(prFileDialogInternal);
    prFileDialogInternal.EndFrame();
    
  }
  void IGFD::FileDialog::QuitFrame()
  {
    QuitThumbnailFrame(prFileDialogInternal);
  }

  void IGFD::FileDialog::prDrawHeader()
  {
#ifdef USE_BOOKMARK
    prDrawBookmarkButton();
    ImGui::SameLine();
#endif // USE_BOOKMARK

    prFileDialogInternal.puFileManager.DrawDirectoryCreation(prFileDialogInternal);
    ImGui::SameLine();
    ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
    ImGui::SameLine();
    prFileDialogInternal.puFileManager.DrawPathComposer(prFileDialogInternal);

#ifdef USE_THUMBNAILS
    if (!(prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableThumbnailMode))
    {
      prDrawDisplayModeToolBar();
      ImGui::SameLine();
      ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
      ImGui::SameLine();
    }
#endif // USE_THUMBNAILS

    prFileDialogInternal.puSearchManager.DrawSearchBar(prFileDialogInternal);
  }

  bool IGFD::FileDialog::prDrawContent()
  {
    bool escape = false;
    ImVec2 size = ImGui::GetContentRegionAvail() - ImVec2(0.0f, prFileDialogInternal.puFooterHeight);

#ifdef USE_BOOKMARK
    if (prBookmarkPaneShown)
    {
      //size.x -= prBookmarkWidth;
      float otherWidth = size.x - prBookmarkWidth;
      ImGui::PushID("##splitterbookmark");
      IGFD::Utils::Splitter(true, 4.0f,
        &prBookmarkWidth, &otherWidth, 10.0f,
        10.0f + prFileDialogInternal.puDLGoptionsPaneWidth, size.y);
      ImGui::PopID();
      size.x -= otherWidth;
      prDrawBookmarkPane(prFileDialogInternal, size);
      ImGui::SameLine();
    }
#endif // USE_BOOKMARK

    size.x = ImGui::GetContentRegionAvail().x - prFileDialogInternal.puDLGoptionsPaneWidth;

    if (prFileDialogInternal.puDLGoptionsPane)
    {
      ImGui::PushID("##splittersidepane");
      IGFD::Utils::Splitter(true, 4.0f, &size.x, &prFileDialogInternal.puDLGoptionsPaneWidth, 10.0f, 10.0f, size.y);
      ImGui::PopID();
    }

#ifdef USE_THUMBNAILS
    if (prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_DisableThumbnailMode)
    {
      prDrawFileListView(size);
    }
    else
    {
      switch (prDisplayMode)
      {
      case DisplayModeEnum::FILE_LIST:
        prDrawFileListView(size);
        break;
      case DisplayModeEnum::THUMBNAILS_LIST:
        prDrawThumbnailsListView(size);
        break;
      case DisplayModeEnum::THUMBNAILS_GRID:
        prDrawThumbnailsGridView(size);
      }
    }
#else
    escape = prDrawFileListView(size);
#endif // USE_THUMBNAILS

    if (prFileDialogInternal.puDLGoptionsPane)
    {
      prDrawSidePane(size.y);
    }

    return escape;
  }

  int IGFD::FileDialog::isFileNameValid(const char* n) {
#ifdef _WIN32
    char lower[6];
    int len=strlen(n);

    for (int i=0; i<6; i++) {
      lower[i]=0;
    }

    if (len>=3) {
      if (len>5) len=5;
      for (int i=0; i<len; i++) {
        if (n[i]>='A' && n[i]<='Z') {
          lower[i]=n[i]+('a'-'A');
        } else {
          lower[i]=n[i];
        }
      }
      if ((memcmp(lower,"con",3)==0 ||
           memcmp(lower,"prn",3)==0 ||
           memcmp(lower,"aux",3)==0 ||
           memcmp(lower,"nul",3)==0) &&
          (len==3 || lower[3]=='.')) {
        return 2;
      }
      if (len>=4) {
        if ((memcmp(lower,"com",3) ||
             memcmp(lower,"lpt",3)) &&
            (lower[3]>='1' && lower[3]<='9') &&
            (len==4 || lower[4]=='.')) {
          return 2;
        }
      }
    }
#endif
    for (const unsigned char* i=(const unsigned char*)n; *i; i++) {
#ifdef _WIN32
      if (*i<32) {
        return 3;
      }
#endif

      switch (*i) {
#ifdef _WIN32
        case '<':
        case '>':
        case ':':
        case '"':
        case '\\':
        case '|':
        case '?':
        case '*':
#endif
        case '/':
          return 1;
      }
    }
    return 0;
  }

  bool IGFD::FileDialog::prDrawFooter()
  {
    auto& fdFile = prFileDialogInternal.puFileManager;
    
    float posY = ImGui::GetCursorPos().y; // height of last bar calc

    ImGui::AlignTextToFramePadding();
    if (!fdFile.puDLGDirectoryMode)
      ImGui::Text("%s",FileDialog::Instance()->fileNameString);
    else // directory chooser
      ImGui::Text("%s",FileDialog::Instance()->dirNameString);

    ImGui::SameLine();

    // Input file fields
    float width = ImGui::GetContentRegionAvail().x;
    // fix this! fix this! fix this!
    if (!fdFile.puDLGDirectoryMode)
      width -= FILTER_COMBO_WIDTH*DpiScale;
    ImGui::PushItemWidth(width);
    ImGui::InputText("##FileName", fdFile.puFileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
    if (ImGui::GetItemID() == ImGui::GetActiveID())
      prFileDialogInternal.puFileInputIsActive = true;
    ImGui::PopItemWidth();

    // combobox of filters
    prFileDialogInternal.puFilterManager.DrawFilterComboBox(prFileDialogInternal);

    bool res = false;

    // OK Button
    bool notEmpty=strlen(fdFile.puFileNameBuffer);
    int fileValid=isFileNameValid(fdFile.puFileNameBuffer);
    if (!(prFileDialogInternal.puDLGflags&ImGuiFileDialogFlags_ConfirmOverwrite)) fileValid=0;
    ImGui::BeginDisabled(!(prFileDialogInternal.puCanWeContinue && notEmpty && fileValid==0));
    std::string okbs=std::string(FileDialog::Instance()->okButtonString)+"##validationdialog";
    if (IMGUI_BUTTON(okbs.c_str()))
    {
      prFileDialogInternal.puIsOk = true;
      res = true;
    }
    ImGui::EndDisabled();
    if (!(prFileDialogInternal.puCanWeContinue && notEmpty && fileValid==0)) {
      if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
        if (!notEmpty) {
          if (prFileDialogInternal.puDLGflags&ImGuiFileDialogFlags_ConfirmOverwrite) {
            ImGui::SetTooltip("file name is empty");
          } else {
            ImGui::SetTooltip("select a file");
          }
        } else if (!prFileDialogInternal.puCanWeContinue) {
          ImGui::SetTooltip("we can't continue - this is most likely a bug!");
        } else switch (fileValid) {
          case 1:
#ifdef _WIN32
            ImGui::SetTooltip("invalid characters in file name\nmake sure there aren't any of these:\n  < > : \" / \\ | ? *");
#else
            ImGui::SetTooltip("invalid characters in file name\nmake sure there aren't any slashes (/)");
#endif
            break;
          case 2:
            ImGui::SetTooltip("this file name is reserved by the system");
            break;
          case 3:
            ImGui::SetTooltip("non-printable characters in file name\nhow did you manage to type these?");
            break;
          default:
            ImGui::SetTooltip("bug! how did you do this? please fill an issue report now...");
            break;
        }
      }
    }

    ImGui::SameLine();

    // Cancel Button
    std::string cbs=std::string(cancelButtonString)+"##validationdialog";
    if (IMGUI_BUTTON(cbs.c_str()) || 
      prFileDialogInternal.puNeedToExitDialog) // dialog exit asked
    {
      prFileDialogInternal.puIsOk = false;
      res = true;
    }

    prFileDialogInternal.puFooterHeight = ImGui::GetCursorPosY() - posY;

    return res;
  }

  // returns 0 if not break loop, 1 if break loop, 2 if exit dialog
  int IGFD::FileDialog::prSelectableItem(int vidx, const FileInfos& vInfos, bool vSelected, const char* vFmt, ...)
  {
    auto& fdi = prFileDialogInternal.puFileManager;

    static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick |
      ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth;

                // TODO BUG?!
                // YES BUG: THIS JUST CRASHED FOR SOME REASON
    va_list args;
    va_start(args, vFmt);
    vsnprintf(fdi.puVariadicBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFmt, args);
    va_end(args);

    float h = /*mobileMode?(ImGui::GetFontSize()+10.0f*DpiScale):*/0.0f;
#ifdef USE_THUMBNAILS
    if (prDisplayMode == DisplayModeEnum::THUMBNAILS_LIST)
      h = DisplayMode_ThumbailsList_ImageHeight;
#endif // USE_THUMBNAILS
#ifdef USE_EXPLORATION_BY_KEYS
    bool flashed = prBeginFlashItem((size_t)vidx);
    bool res = prFlashableSelectable(fdi.puVariadicBuffer, vSelected, selectableFlags,
      flashed, ImVec2(-1.0f, h));
    if (flashed)
      prEndFlashItem();
#else // USE_EXPLORATION_BY_KEYS
    (void)vidx; // remove a warnings ofr unused var

    bool res = ImGui::Selectable(fdi.puVariadicBuffer, vSelected, selectableFlags, ImVec2(-1.0f, h));
#endif // USE_EXPLORATION_BY_KEYS
    if (res)
    {
      if (vInfos.fileType == 'd')
      {
        bool isSelectingDir=false;
        // nav system, selectebale cause open directory or select directory
        if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
        {
          if (fdi.puDLGDirectoryMode) // directory chooser
          {
            fdi.SelectFileName(prFileDialogInternal, vInfos);
          }
          else
          {
            fdi.puPathClicked = fdi.SelectDirectory(vInfos);
            isSelectingDir=true;
          }
        }
        else // no nav system => classic behavior
        {
          if (DOUBLE_CLICKED) // 0 -> left mouse button double click
          {
            isSelectingDir=true;
            fdi.puPathClicked = fdi.SelectDirectory(vInfos);
          }
          else if (fdi.puDLGDirectoryMode) // directory chooser
          {
            fdi.SelectFileName(prFileDialogInternal, vInfos);
          }
        }

        return isSelectingDir; // needToBreakTheloop
      }
      else
      {
        if (DOUBLE_CLICKED) {
          fdi.SelectFileName(prFileDialogInternal, vInfos);
          prFileDialogInternal.puIsOk = true;
          return 2;
        } else {
          fdi.SelectFileName(prFileDialogInternal, vInfos);
          if (prFileDialogInternal.puDLGselFun!=NULL) {
            std::string argPath;
            for (auto& i: GetSelection()) {
              argPath=i.second;
            }
            if (!argPath.empty()) {
              prFileDialogInternal.puDLGselFun(argPath.c_str());
            }
          }
        }
      }
    }

    return 0;
  }

  void IGFD::FileDialog::prBeginFileColorIconStyle(const FileInfos& vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont)
  {
    vOutStr.clear();
    vOutShowColor = false;

    if (vFileInfos.fileStyle.use_count()) //-V807 //-V522
    {
      vOutShowColor = true;

      *vOutFont = vFileInfos.fileStyle->font;
    }

    if (vOutShowColor && !vFileInfos.fileStyle->icon.empty()) vOutStr = vFileInfos.fileStyle->icon;
    else if (vFileInfos.fileType == 'd') vOutStr = dirEntryString;
    else if (vFileInfos.fileType == 'l') vOutStr = linkEntryString;
    else if (vFileInfos.fileType == 'f') vOutStr = fileEntryString;

    vOutStr += " " + vFileInfos.fileNameExt;

    if (vOutShowColor)
      ImGui::PushStyleColor(ImGuiCol_Text, vFileInfos.fileStyle->color);
    if (*vOutFont)
      ImGui::PushFont(*vOutFont);
  }

  void IGFD::FileDialog::prEndFileColorIconStyle(const bool& vShowColor, ImFont* vFont)
  {
    if (vFont)
      ImGui::PopFont();
    if (vShowColor)
      ImGui::PopStyleColor();
  }

  bool IGFD::FileDialog::prDrawFileListView(ImVec2 vSize)
  {
    bool escape = false;
    auto& fdi = prFileDialogInternal.puFileManager;

    ImGui::PushID(this);

    static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg |
      ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY |
      ImGuiTableFlags_NoHostExtendY
#ifndef USE_CUSTOM_SORTING_ICON
      | ImGuiTableFlags_Sortable
#endif // USE_CUSTOM_SORTING_ICON
      ;
    auto listViewID = ImGui::GetID("##FileDialog_fileTable");
    if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 4, flags, vSize, 0.0f)) //-V112
    {
      ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible
      ImGui::TableSetupColumn(fdi.puHeaderFileName.c_str(), ImGuiTableColumnFlags_WidthStretch, -1, 0);
      ImGui::TableSetupColumn(fdi.puHeaderFileType.c_str(), ImGuiTableColumnFlags_WidthFixed | 
        ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 1);
      ImGui::TableSetupColumn(fdi.puHeaderFileSize.c_str(), ImGuiTableColumnFlags_WidthFixed | 
        ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 2);
      ImGui::TableSetupColumn(fdi.puHeaderFileDate.c_str(), ImGuiTableColumnFlags_WidthFixed | 
        ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 3);

#ifndef USE_CUSTOM_SORTING_ICON
      // Sort our data if sort specs have been changed!
      if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs())
      {
        if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty())
        {
          if (sorts_specs->Specs->ColumnUserID == 0)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true);
          else if (sorts_specs->Specs->ColumnUserID == 1)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true);
          else if (sorts_specs->Specs->ColumnUserID == 2)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true);
          else //if (sorts_specs->Specs->ColumnUserID == 3) => alwayd true for the moment, to uncomment if we add a fourth column
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true);

          sorts_specs->SpecsDirty = false;
        }
      }

      ImGui::TableHeadersRow();
#else // USE_CUSTOM_SORTING_ICON
      ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
      for (int column = 0; column < 4; column++) //-V112
      {
        ImGui::TableSetColumnIndex(column);
        const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()
        ImGui::PushID(column);
        ImGui::TableHeader(column_name);
        ImGui::PopID();
        if (ImGui::IsItemClicked())
        {
          if (column == 0)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true);
          else if (column == 1)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true);
          else if (column == 2)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true);
          else //if (column == 3) => alwayd true for the moment, to uncomment if we add a fourth column
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true);
        }
      }
#endif // USE_CUSTOM_SORTING_ICON
      if (!fdi.IsFilteredListEmpty())
      {
        std::string _str;
        ImFont* _font = nullptr;
        bool _showColor = false;
        
        prFileListClipper.Begin((int)fdi.GetFilteredListSize(), ImGui::GetTextLineHeightWithSpacing());
        while (prFileListClipper.Step())
        {
          for (int i = prFileListClipper.DisplayStart; i < prFileListClipper.DisplayEnd; i++)
          {
            if (i < 0) continue;

            const FileInfos& infos = fdi.GetFilteredFileAt((size_t)i);
            if (!infos.isValid)
              continue;

            prBeginFileColorIconStyle(infos, _showColor, _str, &_font);
          
            bool selected = fdi.IsFileNameSelected(infos.fileNameExt); // found

            ImGui::TableNextRow();

            int needToBreakTheloop = false;

            if (ImGui::TableNextColumn()) // file name
            {
             // TODO BUG?!?!?!
             // YES BUG
              needToBreakTheloop = prSelectableItem(i, infos, selected, "%s", _str.c_str());
              if (needToBreakTheloop==2) escape=true;
            }
            if (ImGui::TableNextColumn()) // file type
            {
              ImGui::Text("%s", infos.fileExt.c_str());
            }
            if (ImGui::TableNextColumn()) // file size
            {
              if (infos.fileType != 'd')
              {
                ImGui::Text("%s ", infos.formatedFileSize.c_str());
              }
              else
              {
                ImGui::Text("%s","");
              }
            }
            if (ImGui::TableNextColumn()) // file date + time
            {
              ImGui::Text("%s", infos.fileModifDate.c_str());
            }

            prEndFileColorIconStyle(_showColor, _font);

            if (needToBreakTheloop==1)
              break;
          }
        }
        prFileListClipper.End();
      }

#ifdef USE_EXPLORATION_BY_KEYS
      if (!fdi.puInputPathActivated)
      {
        prLocateByInputKey(prFileDialogInternal);
        prExploreWithkeys(prFileDialogInternal, listViewID);
      }
#endif // USE_EXPLORATION_BY_KEYS

      ImGuiContext& g = *GImGui;
      if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID)
      {
        prFileDialogInternal.puFileListViewIsActive = true;
      }

      ImGui::EndTable();
    }

    ImGui::PopID();
    return escape;
  }

#ifdef USE_THUMBNAILS
  void IGFD::FileDialog::prDrawThumbnailsListView(ImVec2 vSize)
  {
    auto& fdi = prFileDialogInternal.puFileManager;

    ImGui::PushID(this);

    static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg |
      ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY |
      ImGuiTableFlags_NoHostExtendY
#ifndef USE_CUSTOM_SORTING_ICON
      | ImGuiTableFlags_Sortable
#endif // USE_CUSTOM_SORTING_ICON
      ;
    auto listViewID = ImGui::GetID("##FileDialog_fileTable");
    if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 5, flags, vSize, 0.0f))
    {
      ImGui::TableSetupScrollFreeze(0, 1); // Make header always visible
      ImGui::TableSetupColumn(fdi.puHeaderFileName.c_str(), ImGuiTableColumnFlags_WidthStretch, -1, 0);
      ImGui::TableSetupColumn(fdi.puHeaderFileType.c_str(), ImGuiTableColumnFlags_WidthFixed |
        ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 1);
      ImGui::TableSetupColumn(fdi.puHeaderFileSize.c_str(), ImGuiTableColumnFlags_WidthFixed |
        ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 2);
      ImGui::TableSetupColumn(fdi.puHeaderFileDate.c_str(), ImGuiTableColumnFlags_WidthFixed |
        ((prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0), -1, 3);
      // not needed to have an option for hide the thumbnails since this is why this view is used
      ImGui::TableSetupColumn(fdi.puHeaderFileThumbnails.c_str(), ImGuiTableColumnFlags_WidthFixed, -1, 4); //-V112

#ifndef USE_CUSTOM_SORTING_ICON
      // Sort our data if sort specs have been changed!
      if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs())
      {
        if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty())
        {
          if (sorts_specs->Specs->ColumnUserID == 0)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true);
          else if (sorts_specs->Specs->ColumnUserID == 1)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true);
          else if (sorts_specs->Specs->ColumnUserID == 2)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true);
          else if (sorts_specs->Specs->ColumnUserID == 3)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true);
          else // if (sorts_specs->Specs->ColumnUserID == 4) = > always true for the moment, to uncomment if we add another column
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS, true);
          sorts_specs->SpecsDirty = false;
        }
      }

      ImGui::TableHeadersRow();
#else // USE_CUSTOM_SORTING_ICON
      ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
      for (int column = 0; column < 5; column++)
      {
        ImGui::TableSetColumnIndex(column);
        const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()
        ImGui::PushID(column);
        ImGui::TableHeader(column_name);
        ImGui::PopID();
        if (ImGui::IsItemClicked())
        {
          if (column == 0)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME, true);
          else if (column == 1)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_TYPE, true);
          else if (column == 2)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_SIZE, true);
          else if (column == 3)
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_DATE, true);
          else // if (column == 4) = > always true for the moment, to uncomment if we add another column
            fdi.SortFields(prFileDialogInternal, IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS, true);
        }
      }
#endif // USE_CUSTOM_SORTING_ICON
      if (!fdi.IsFilteredListEmpty())
      {
        std::string _str;
        ImFont* _font = nullptr;
        bool _showColor = false;

        ImGuiContext& g = *GImGui;
        const float itemHeight = ImMax(g.FontSize, DisplayMode_ThumbailsList_ImageHeight) + g.Style.ItemSpacing.y;

        prFileListClipper.Begin((int)fdi.GetFilteredListSize(), itemHeight);
        while (prFileListClipper.Step())
        {
          for (int i = prFileListClipper.DisplayStart; i < prFileListClipper.DisplayEnd; i++)
          {
            if (i < 0) continue;

            auto infos = fdi.GetFilteredFileAt((size_t)i);
            if (!infos.use_count())
              continue;

            prBeginFileColorIconStyle(infos, _showColor, _str, &_font);

            bool selected = fdi.IsFileNameSelected(infos->fileNameExt); // found

            ImGui::TableNextRow();

            bool needToBreakTheloop = false;

            if (ImGui::TableNextColumn()) // file name
            {
              needToBreakTheloop = prSelectableItem(i, infos, selected, _str.c_str());
            }
            if (ImGui::TableNextColumn()) // file type
            {
              ImGui::Text("%s", infos->fileExt.c_str());
            }
            if (ImGui::TableNextColumn()) // file size
            {
              if (infos->fileType != 'd')
              {
                ImGui::Text("%s ", infos->formatedFileSize.c_str());
              }
              else
              {
                ImGui::Text("");
              }
            }
            if (ImGui::TableNextColumn()) // file date + time
            {
              ImGui::Text("%s", infos->fileModifDate.c_str());
            }
            if (ImGui::TableNextColumn()) // file thumbnails
            {
              auto th = &infos->thumbnailInfo;

              if (!th->isLoadingOrLoaded)
              {
                prAddThumbnailToLoad(infos);
              }
              if (th->isReadyToDisplay &&
                th->textureID)
              {
                ImGui::Image((ImTextureID)th->textureID,
                  ImVec2((float)th->textureWidth,
                    (float)th->textureHeight));
              }
            }

            prEndFileColorIconStyle(_showColor, _font);

            if (needToBreakTheloop)
              break;
          }
        }
        prFileListClipper.End();
      }

#ifdef USE_EXPLORATION_BY_KEYS
      if (!fdi.puInputPathActivated)
      {
        prLocateByInputKey(prFileDialogInternal);
        prExploreWithkeys(prFileDialogInternal, listViewID);
      }
#endif // USE_EXPLORATION_BY_KEYS

      ImGuiContext& g = *GImGui;
      if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID)
      {
        prFileDialogInternal.puFileListViewIsActive = true;
      }

      ImGui::EndTable();
    }

    ImGui::PopID();
  }

  void IGFD::FileDialog::prDrawThumbnailsGridView(ImVec2 vSize)
  {
    if (ImGui::BeginChild("##thumbnailsGridsFiles", vSize))
    {
      // todo
    }

    ImGui::EndChild();
  }

#endif

  void IGFD::FileDialog::prDrawSidePane(float vHeight)
  {
    ImGui::SameLine();

    ImGui::BeginChild("##FileTypes", ImVec2(0, vHeight));

    prFileDialogInternal.puDLGoptionsPane(
      prFileDialogInternal.puFilterManager.GetSelectedFilter().filter.c_str(), 
      prFileDialogInternal.puDLGuserDatas, &prFileDialogInternal.puCanWeContinue);

    ImGui::EndChild();
  }

  void IGFD::FileDialog::Close()
  {
    prFileDialogInternal.puDLGkey.clear();
    prFileDialogInternal.puShowDialog = false;
  }

  bool IGFD::FileDialog::WasOpenedThisFrame(const std::string& vKey) const
  {
    bool res = prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey;
    if (res)
    {
      ImGuiContext& g = *GImGui;
      res &= prFileDialogInternal.puLastImGuiFrameCount == g.FrameCount; // return true if a dialog was displayed in this frame
    }
    return res;
  }

  bool IGFD::FileDialog::WasOpenedThisFrame() const
  {
    bool res = prFileDialogInternal.puShowDialog;
    if (res)
    {
      ImGuiContext& g = *GImGui;
      res &= prFileDialogInternal.puLastImGuiFrameCount == g.FrameCount; // return true if a dialog was displayed in this frame
    }
    return res;
  }

  bool IGFD::FileDialog::IsOpened(const std::string& vKey) const
  {
    return (prFileDialogInternal.puShowDialog && prFileDialogInternal.puDLGkey == vKey);
  }

  bool IGFD::FileDialog::IsOpened() const
  {
    return prFileDialogInternal.puShowDialog;
  }

  std::string IGFD::FileDialog::GetOpenedKey() const
  {
    if (prFileDialogInternal.puShowDialog)
      return prFileDialogInternal.puDLGkey;
    return "";
  }

  std::string IGFD::FileDialog::GetFilePathName()
  {
    return prFileDialogInternal.puFileManager.GetResultingFilePathName(prFileDialogInternal);
  }

  std::string IGFD::FileDialog::GetCurrentPath()
  {
    return prFileDialogInternal.puFileManager.GetResultingPath();
  }

  std::string IGFD::FileDialog::GetCurrentFileName()
  {
    return prFileDialogInternal.puFileManager.GetResultingFileName(prFileDialogInternal);
  }

  std::string IGFD::FileDialog::GetCurrentFilter()
  {
    return prFileDialogInternal.puFilterManager.GetSelectedFilter().filter;
  }

  std::map<std::string, std::string> IGFD::FileDialog::GetSelection()
  {
    return prFileDialogInternal.puFileManager.GetResultingSelection();
  }

  UserDatas IGFD::FileDialog::GetUserDatas() const
  {
    return prFileDialogInternal.puDLGuserDatas;
  }

  bool IGFD::FileDialog::IsOk() const
  {
    return prFileDialogInternal.puIsOk;
  }

  void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos)
  {
    prFileDialogInternal.puFilterManager.SetFileStyle(vFlags, vCriteria, vInfos);
  }

  void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont)
  {
    prFileDialogInternal.puFilterManager.SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont);
  }

  bool IGFD::FileDialog::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont **vOutFont)
  {
    return prFileDialogInternal.puFilterManager.GetFileStyle(vFlags, vCriteria, vOutColor, vOutIcon, vOutFont);
  }

  void IGFD::FileDialog::ClearFilesStyle()
  {
    prFileDialogInternal.puFilterManager.ClearFilesStyle();
  }

  void IGFD::FileDialog::SetLocales(const int& vLocaleCategory, const std::string& vLocaleBegin, const std::string& vLocaleEnd)
  {
    prFileDialogInternal.puUseCustomLocale = true;
    prFileDialogInternal.puLocaleBegin = vLocaleBegin;
    prFileDialogInternal.puLocaleEnd = vLocaleEnd;
  }

  //////////////////////////////////////////////////////////////////////////////
  //// OVERWRITE DIALOG ////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////

  bool IGFD::FileDialog::prConfirm_Or_OpenOverWriteFileDialog_IfNeeded(bool vLastAction, ImGuiWindowFlags vFlags)
  {
    // if confirmation => return true for confirm the overwrite et quit the dialog
    // if cancel => return false && set IsOk to false for keep inside the dialog

    // if IsOk == false => return false for quit the dialog
    if (!prFileDialogInternal.puIsOk && vLastAction)
    {
      QuitFrame();
      return true;
    }

    // if IsOk == true && no check of overwrite => return true for confirm the dialog
    if (prFileDialogInternal.puIsOk && vLastAction && !(prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_ConfirmOverwrite))
    {
      QuitFrame();
      return true;
    }

    // if IsOk == true && check of overwrite => return false and show confirm to overwrite dialog
    if ((prFileDialogInternal.puOkResultToConfirm || (prFileDialogInternal.puIsOk && vLastAction)) && 
      (prFileDialogInternal.puDLGflags & ImGuiFileDialogFlags_ConfirmOverwrite))
    {
      if (prFileDialogInternal.puIsOk) // catched only one time
      {
        if (!prFileDialogInternal.puFileManager.IsFileExist(GetFilePathName(),prFileDialogInternal.puFilterManager.GetSelectedFilter().firstFilter)) // not existing => quit dialog
        {
          QuitFrame();
          return true;
        }
        else // existing => confirm dialog to open
        {
          prFileDialogInternal.puIsOk = false;
          prFileDialogInternal.puOkResultToConfirm = true;
        }
      }

      std::string name = std::string(OverWriteDialogTitleString) + "##" + prFileDialogInternal.puDLGtitle + prFileDialogInternal.puDLGkey + "OverWriteDialog";

      bool res = false;

      ImGui::OpenPopup(name.c_str());
      if (ImGui::BeginPopupModal(name.c_str(), (bool*)0,
        vFlags | ImGuiWindowFlags_AlwaysAutoResize |
        ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
      {
        ImGui::SetWindowPos(prFileDialogInternal.puDialogCenterPos - ImGui::GetWindowSize() * 0.5f); // next frame needed for GetWindowSize to work

        ImGui::Text("%s", OverWriteDialogMessageString);

        if (IMGUI_BUTTON(OverWriteDialogConfirmButtonString))
        {
          prFileDialogInternal.puOkResultToConfirm = false;
          prFileDialogInternal.puIsOk = true;
          res = true;
          ImGui::CloseCurrentPopup();
        }

        ImGui::SameLine();

        if (IMGUI_BUTTON(OverWriteDialogCancelButtonString))
        {
          prFileDialogInternal.puOkResultToConfirm = false;
          prFileDialogInternal.puIsOk = false;
          res = false;
          ImGui::CloseCurrentPopup();
        }

        ImGui::EndPopup();
      }

      if (res)
      {
        QuitFrame();
      }
      return res;
    }

    return false;
  }
}

#endif // __cplusplus

/////////////////////////////////////////////////////////////////
///// C Interface ///////////////////////////////////////////////
/////////////////////////////////////////////////////////////////

// Return an initialized IGFD_Selection_Pair
IMGUIFILEDIALOG_API IGFD_Selection_Pair IGFD_Selection_Pair_Get(void)
{
  IGFD_Selection_Pair res = {};
  res.fileName = nullptr;
  res.filePathName = nullptr;
  return res;
}

// destroy only the content of vSelection_Pair
IMGUIFILEDIALOG_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair* vSelection_Pair)
{
  if (vSelection_Pair)
  {
    delete[] vSelection_Pair->fileName;
    delete[] vSelection_Pair->filePathName;
  }
}

// Return an initialized IGFD_Selection
IMGUIFILEDIALOG_API IGFD_Selection IGFD_Selection_Get(void)
{
  return { nullptr, 0U };
}

// destroy only the content of vSelection
IMGUIFILEDIALOG_API void IGFD_Selection_DestroyContent(IGFD_Selection* vSelection)
{
  if (vSelection)
  {
    if (vSelection->table)
    {
      for (size_t i = 0U; i < vSelection->count; i++)
      {
        IGFD_Selection_Pair_DestroyContent(&vSelection->table[i]);
      }
      delete[] vSelection->table;
    }
    vSelection->count = 0U;
  }
}

// create an instance of ImGuiFileDialog
IMGUIFILEDIALOG_API ImGuiFileDialog* IGFD_Create(void)
{
  return new ImGuiFileDialog();
}

// destroy the instance of ImGuiFileDialog
IMGUIFILEDIALOG_API void IGFD_Destroy(ImGuiFileDialog* vContext)
{
  if (vContext)
  {
    delete vContext;
    vContext = nullptr;
  }
}

// standard dialog
IMGUIFILEDIALOG_API void IGFD_OpenDialog(
  ImGuiFileDialog* vContext,
  const char* vKey,
  const char* vTitle,
  const char* vFilters,
  const char* vPath,
  const char* vFileName,
  const int vCountSelectionMax,
  void* vUserDatas,
  ImGuiFileDialogFlags flags)
{
  if (vContext)
  {
    vContext->OpenDialog(
      vKey, vTitle, vFilters, vPath, vFileName,
      vCountSelectionMax, vUserDatas, flags);
  }
}

IMGUIFILEDIALOG_API void IGFD_OpenDialog2(
  ImGuiFileDialog* vContext,
  const char* vKey,
  const char* vTitle,
  const char* vFilters,
  const char* vFilePathName,
  const int vCountSelectionMax,
  void* vUserDatas,
  ImGuiFileDialogFlags flags)
{
  if (vContext)
  {
    vContext->OpenDialog(
      vKey, vTitle, vFilters, vFilePathName,
      vCountSelectionMax, vUserDatas, flags);
  }
}

IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog(
  ImGuiFileDialog* vContext,
  const char* vKey,
  const char* vTitle,
  const char* vFilters,
  const char* vPath,
  const char* vFileName,
  IGFD_PaneFun vSidePane,
  const float vSidePaneWidth,
  const int vCountSelectionMax,
  void* vUserDatas,
  ImGuiFileDialogFlags flags)
{
  if (vContext)
  {
    vContext->OpenDialog(
      vKey, vTitle, vFilters,
      vPath, vFileName,
      vSidePane, vSidePaneWidth,
      vCountSelectionMax, vUserDatas, flags);
  }
}

IMGUIFILEDIALOG_API void IGFD_OpenPaneDialog2(
  ImGuiFileDialog* vContext,
  const char* vKey,
  const char* vTitle,
  const char* vFilters,
  const char* vFilePathName,
  IGFD_PaneFun vSidePane,
  const float vSidePaneWidth,
  const int vCountSelectionMax,
  void* vUserDatas,
  ImGuiFileDialogFlags flags)
{
  if (vContext)
  {
    vContext->OpenDialog(
      vKey, vTitle, vFilters,
      vFilePathName,
      vSidePane, vSidePaneWidth,
      vCountSelectionMax, vUserDatas, flags);
  }
}

// modal dialog
IMGUIFILEDIALOG_API void IGFD_OpenModal(
  ImGuiFileDialog* vContext,
  const char* vKey,
  const char* vTitle,
  const char* vFilters,
  const char* vPath,
  const char* vFileName,
  const int vCountSelectionMax,
  void* vUserDatas,
  ImGuiFileDialogFlags flags)
{
  if (vContext)
  {
    vContext->OpenModal(
      vKey, vTitle, vFilters, vPath, vFileName,
      vCountSelectionMax, vUserDatas, flags);
  }
}

IMGUIFILEDIALOG_API void IGFD_OpenModal2(
  ImGuiFileDialog* vContext,
  const char* vKey,
  const char* vTitle,
  const char* vFilters,
  const char* vFilePathName,
  const int vCountSelectionMax,
  void* vUserDatas,
  ImGuiFileDialogFlags flags)
{
  if (vContext)
  {
    vContext->OpenModal(
      vKey, vTitle, vFilters, vFilePathName,
      vCountSelectionMax, vUserDatas, flags);
  }
}

IMGUIFILEDIALOG_API void IGFD_OpenPaneModal(
  ImGuiFileDialog* vContext,
  const char* vKey,
  const char* vTitle,
  const char* vFilters,
  const char* vPath,
  const char* vFileName,
  IGFD_PaneFun vSidePane,
  const float vSidePaneWidth,
  const int vCountSelectionMax,
  void* vUserDatas,
  ImGuiFileDialogFlags flags)
{
  if (vContext)
  {
    vContext->OpenModal(
      vKey, vTitle, vFilters,
      vPath, vFileName,
      vSidePane, vSidePaneWidth,
      vCountSelectionMax, vUserDatas, flags);
  }
}

IMGUIFILEDIALOG_API void IGFD_OpenPaneModal2(
  ImGuiFileDialog* vContext,
  const char* vKey,
  const char* vTitle,
  const char* vFilters,
  const char* vFilePathName,
  IGFD_PaneFun vSidePane,
  const float vSidePaneWidth,
  const int vCountSelectionMax,
  void* vUserDatas,
  ImGuiFileDialogFlags flags)
{
  if (vContext)
  {
    vContext->OpenModal(
      vKey, vTitle, vFilters,
      vFilePathName,
      vSidePane, vSidePaneWidth,
      vCountSelectionMax, vUserDatas, flags);
  }
}

IMGUIFILEDIALOG_API bool IGFD_DisplayDialog(ImGuiFileDialog* vContext,
  const char* vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize)
{
  if (vContext)
  {
    return vContext->Display(vKey, vFlags, vMinSize, vMaxSize);
  }

  return false;
}

IMGUIFILEDIALOG_API void IGFD_CloseDialog(ImGuiFileDialog* vContext)
{
  if (vContext)
  {
    vContext->Close();
  }
}

IMGUIFILEDIALOG_API bool IGFD_IsOk(ImGuiFileDialog* vContext)
{
  if (vContext)
  {
    return vContext->IsOk();
  }

  return false;
}

IMGUIFILEDIALOG_API bool IGFD_WasKeyOpenedThisFrame(ImGuiFileDialog* vContext,
  const char* vKey)
{
  if (vContext)
  {
    vContext->WasOpenedThisFrame(vKey);
  }

  return false;
}

IMGUIFILEDIALOG_API bool IGFD_WasOpenedThisFrame(ImGuiFileDialog* vContext)
{
  if (vContext)
  {
    vContext->WasOpenedThisFrame();
  }

  return false;
}

IMGUIFILEDIALOG_API bool IGFD_IsKeyOpened(ImGuiFileDialog* vContext,
  const char* vCurrentOpenedKey)
{
  if (vContext)
  {
    vContext->IsOpened(vCurrentOpenedKey);
  }

  return false;
}

IMGUIFILEDIALOG_API bool IGFD_IsOpened(ImGuiFileDialog* vContext)
{
  if (vContext)
  {
    vContext->IsOpened();
  }

  return false;
}

IMGUIFILEDIALOG_API IGFD_Selection IGFD_GetSelection(ImGuiFileDialog* vContext)
{
  IGFD_Selection res = IGFD_Selection_Get();

  if (vContext)
  {
    auto sel = vContext->GetSelection();
    if (!sel.empty())
    {
      res.count = sel.size();
      res.table = new IGFD_Selection_Pair[res.count];

      size_t idx = 0U;
      for (const auto& s : sel)
      {
        IGFD_Selection_Pair* pair = res.table + idx++;

        // fileNameExt
        if (!s.first.empty())
        {
          size_t siz = s.first.size() + 1U;
          pair->fileName = new char[siz];
#ifndef MSVC
          strncpy(pair->fileName, s.first.c_str(), siz);
#else
          strncpy_s(pair->fileName, siz, s.first.c_str(), siz);
#endif
          pair->fileName[siz - 1U] = '\0';
        }

        // filePathName
        if (!s.second.empty())
        {
          size_t siz = s.first.size() + 1U;
          pair->filePathName = new char[siz];
#ifndef MSVC
          strncpy(pair->filePathName, s.first.c_str(), siz);
#else
          strncpy_s(pair->filePathName, siz, s.first.c_str(), siz);
#endif
          pair->filePathName[siz - 1U] = '\0';
        }
      }

      return res;
    }
  }

  return res;
}

IMGUIFILEDIALOG_API char* IGFD_GetFilePathName(ImGuiFileDialog* vContext)
{
  char* res = nullptr;

  if (vContext)
  {
    auto s = vContext->GetFilePathName();
    if (!s.empty())
    {
      size_t siz = s.size() + 1U;
      res = new char[siz];
#ifndef MSVC
      strncpy(res, s.c_str(), siz);
#else
      strncpy_s(res, siz, s.c_str(), siz);
#endif
      res[siz - 1U] = '\0';
    }
  }

  return res;
}

IMGUIFILEDIALOG_API char* IGFD_GetCurrentFileName(ImGuiFileDialog* vContext)
{
  char* res = nullptr;

  if (vContext)
  {
    auto s = vContext->GetCurrentFileName();
    if (!s.empty())
    {
      size_t siz = s.size() + 1U;
      res = new char[siz];
#ifndef MSVC
      strncpy(res, s.c_str(), siz);
#else
      strncpy_s(res, siz, s.c_str(), siz);
#endif
      res[siz - 1U] = '\0';
    }
  }

  return res;
}

IMGUIFILEDIALOG_API char* IGFD_GetCurrentPath(ImGuiFileDialog* vContext)
{
  char* res = nullptr;

  if (vContext)
  {
    auto s = vContext->GetCurrentPath();
    if (!s.empty())
    {
      size_t siz = s.size() + 1U;
      res = new char[siz];
#ifndef MSVC
      strncpy(res, s.c_str(), siz);
#else
      strncpy_s(res, siz, s.c_str(), siz);
#endif
      res[siz - 1U] = '\0';
    }
  }

  return res;
}

IMGUIFILEDIALOG_API char* IGFD_GetCurrentFilter(ImGuiFileDialog* vContext)
{
  char* res = nullptr;

  if (vContext)
  {
    auto s = vContext->GetCurrentFilter();
    if (!s.empty())
    {
      size_t siz = s.size() + 1U;
      res = new char[siz];
#ifndef MSVC
      strncpy(res, s.c_str(), siz);
#else
      strncpy_s(res, siz, s.c_str(), siz);
#endif
      res[siz - 1U] = '\0';
    }
  }

  return res;
}

IMGUIFILEDIALOG_API void* IGFD_GetUserDatas(ImGuiFileDialog* vContext)
{
  if (vContext)
  {
    return vContext->GetUserDatas();
  }

  return nullptr;
}

IMGUIFILEDIALOG_API void IGFD_SetFileStyle(ImGuiFileDialog* vContext,
  IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4 vColor, const char* vIcon, ImFont* vFont) //-V813
{
  if (vContext)
  {
    vContext->SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont);
  }
}

IMGUIFILEDIALOG_API void IGFD_SetFileStyle2(ImGuiFileDialog* vContext,
  IGFD_FileStyleFlags vFlags, const char* vCriteria, float vR, float vG, float vB, float vA, const char* vIcon, ImFont* vFont)
{
  if (vContext)
  {
    vContext->SetFileStyle(vFlags, vCriteria, ImVec4(vR, vG, vB, vA), vIcon, vFont);
  }
}

IMGUIFILEDIALOG_API bool IGFD_GetFileStyle(ImGuiFileDialog* vContext,
  IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4* vOutColor, char** vOutIcon, ImFont** vOutFont)
{
  if (vContext)
  {
    std::string icon;
    bool res = vContext->GetFileStyle(vFlags, vCriteria, vOutColor, &icon, vOutFont);
    if (!icon.empty() && vOutIcon)
    {
      size_t siz = icon.size() + 1U;
      *vOutIcon = new char[siz];
#ifndef MSVC
      strncpy(*vOutIcon, icon.c_str(), siz);
#else
      strncpy_s(*vOutIcon, siz, icon.c_str(), siz);
#endif
      (*vOutIcon)[siz - 1U] = '\0';
    }
    return res;
  }

  return false;
}

IMGUIFILEDIALOG_API void IGFD_ClearFilesStyle(ImGuiFileDialog* vContext)
{
  if (vContext)
  {
    vContext->ClearFilesStyle();
  }
}

IMGUIFILEDIALOG_API void SetLocales(ImGuiFileDialog* vContext, const int vCategory, const char* vBeginLocale, const char* vEndLocale)
{
  if (vContext)
  {
    vContext->SetLocales(vCategory, (vBeginLocale ? vBeginLocale : ""), (vEndLocale ? vEndLocale : ""));
  }
}

#ifdef USE_EXPLORATION_BY_KEYS
IMGUIFILEDIALOG_API void IGFD_SetFlashingAttenuationInSeconds(ImGuiFileDialog* vContext, float vAttenValue)
{
  if (vContext)
  {
    vContext->SetFlashingAttenuationInSeconds(vAttenValue);
  }
}
#endif

#ifdef USE_BOOKMARK
IMGUIFILEDIALOG_API char* IGFD_SerializeBookmarks(ImGuiFileDialog* vContext)
{
  char* res = nullptr;

  if (vContext)
  {
    auto s = vContext->SerializeBookmarks();
    if (!s.empty())
    {
      size_t siz = s.size() + 1U;
      res = new char[siz];
#ifndef MSVC
      strncpy(res, s.c_str(), siz);
#else
      strncpy_s(res, siz, s.c_str(), siz);
#endif
      res[siz - 1U] = '\0';
    }
  }

  return res;
}

IMGUIFILEDIALOG_API void IGFD_DeserializeBookmarks(ImGuiFileDialog* vContext, const char* vBookmarks)
{
  if (vContext)
  {
    vContext->DeserializeBookmarks(vBookmarks);
  }
}
#endif

#ifdef USE_THUMBNAILS
IMGUIFILEDIALOG_API void SetCreateThumbnailCallback(ImGuiFileDialog* vContext, const IGFD_CreateThumbnailFun vCreateThumbnailFun)
{
  if (vContext)
  {
    vContext->SetCreateThumbnailCallback(vCreateThumbnailFun);
  }
}

IMGUIFILEDIALOG_API void SetDestroyThumbnailCallback(ImGuiFileDialog* vContext, const IGFD_DestroyThumbnailFun vDestroyThumbnailFun)
{
  if (vContext)
  {
    vContext->SetDestroyThumbnailCallback(vDestroyThumbnailFun);
  }
}

IMGUIFILEDIALOG_API void ManageGPUThumbnails(ImGuiFileDialog* vContext)
{
  if (vContext)
  {
    vContext->ManageGPUThumbnails();
  }
}
#endif // USE_THUMBNAILS
