5#include "hellfire/utilities/FileDialog.h"
18 std::string
FileDialog::win32_open_file(
const std::vector<FileFilter> &filters,
19 const std::filesystem::path &default_path) {
23 CHAR szFile[260] = {};
24 ZeroMemory(&ofn,
sizeof(ofn));
25 ofn.lStructSize =
sizeof(ofn);
26 ofn.hwndOwner = GetActiveWindow();
27 ofn.lpstrFile = szFile;
28 ofn.nMaxFile =
sizeof(szFile);
31 std::string filterStr;
32 if (filters.empty()) {
34 filterStr =
"All Files\0*.*\0";
36 for (
const auto &filter: filters) {
37 filterStr += filter.name +
'\0' + filter.extensions +
'\0';
42 std::vector filterBuf(filterStr.begin(), filterStr.end());
43 filterBuf.push_back(
'\0');
45 ofn.lpstrFilter = filterBuf.data();
47 ofn.lpstrFileTitle =
nullptr;
48 ofn.nMaxFileTitle = 0;
49 ofn.lpstrInitialDir =
nullptr;
50 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
52 std::string initialDir;
53 if (!default_path.empty()) {
54 initialDir = default_path.string();
55 std::ranges::replace(initialDir,
'/',
'\\');
56 ofn.lpstrInitialDir = initialDir.c_str();
59 if (GetOpenFileNameA(&ofn) == TRUE) {
60 filepath = ofn.lpstrFile;
62 DWORD err = CommDlgExtendedError();
63 std::cerr << err <<
'\n';
69 std::string
FileDialog::win32_save_file(
const std::string &default_filename,
70 const std::vector<FileFilter> &filters, std::string &save_name_to,
71 const std::filesystem::path &default_path) {
75 CHAR szFile[260] = {};
77 if (!default_path.empty()) {
78 std::string dir = default_path.string();
79 std::replace(dir.begin(), dir.end(),
'/',
'\\');
81 if (!default_filename.empty()) {
82 std::string full = dir +
"\\" + default_filename;
83 strncpy(szFile, full.c_str(),
sizeof(szFile) - 1);
86 strncpy(szFile, dir.c_str(),
sizeof(szFile) - 1);
90 ZeroMemory(&ofn,
sizeof(ofn));
91 ofn.lStructSize =
sizeof(ofn);
92 ofn.hwndOwner = GetActiveWindow();
93 ofn.lpstrFile = szFile;
94 ofn.nMaxFile =
sizeof(szFile);
97 std::string filterStr;
98 std::string defaultExt;
100 if (filters.empty()) {
102 filterStr =
"All Files\0*.*\0";
104 for (
const auto &[name, extensions]: filters) {
105 filterStr += name +
'\0' + extensions +
'\0';
108 if (defaultExt.empty() && !extensions.empty()) {
110 size_t pos = extensions.find(
"*.");
111 if (pos != std::string::npos) {
112 size_t endPos = extensions.find(
';', pos);
113 if (endPos == std::string::npos) {
114 endPos = extensions.size();
117 defaultExt = extensions.substr(pos + 2, endPos - pos - 2);
124 std::vector filterBuf(filterStr.begin(), filterStr.end());
125 filterBuf.push_back(
'\0');
127 ofn.lpstrFilter = filterBuf.data();
128 ofn.nFilterIndex = 1;
129 ofn.lpstrFileTitle =
nullptr;
130 ofn.lpstrInitialDir =
nullptr;
131 ofn.nMaxFileTitle = 0;
132 ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
135 std::vector extBuf(defaultExt.begin(), defaultExt.end());
136 if (!defaultExt.empty()) {
138 extBuf.push_back(
'\0');
139 ofn.lpstrDefExt = extBuf.data();
142 if (GetSaveFileNameA(&ofn) == TRUE) {
143 filepath = ofn.lpstrFile;
146 size_t lastSlash = filepath.find_last_of(
"\\/");
147 if (lastSlash != std::string::npos) {
148 save_name_to = filepath.substr(lastSlash + 1);
150 save_name_to = filepath;
153 DWORD err = CommDlgExtendedError();
154 std::cerr << err <<
'\n';
160 std::string
FileDialog::imgui_open_file(
const std::vector<FileFilter> &filters) {
161 static std::string filepath;
165 std::string filterDesc =
"Supported formats: ";
166 if (filters.empty()) {
167 filterDesc +=
"All files";
169 for (size_t i = 0; i < filters.size(); ++i) {
170 filterDesc += filters[i].name;
171 if (i < filters.size() - 1) {
177 ImGui::OpenPopup(
"Open File");
179 if (ImGui::BeginPopupModal(
"Open File",
nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
180 static char buf[512] =
"assets/";
181 ImGui::Text(
"%s", filterDesc.c_str());
182 ImGui::InputText(
"Path to file", buf, 512);
184 if (ImGui::Button(
"OK", ImVec2(120, 0))) {
186 ImGui::CloseCurrentPopup();
189 if (ImGui::Button(
"Cancel", ImVec2(120, 0))) {
190 ImGui::CloseCurrentPopup();
199 std::string folder_path;
202 HRESULT hr = CoInitializeEx(
nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
207 IFileDialog *pfd =
nullptr;
208 hr = CoCreateInstance(CLSID_FileOpenDialog,
nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
213 hr = pfd->GetOptions(&dwOptions);
216 hr = pfd->SetOptions(dwOptions | FOS_PICKFOLDERS);
220 if (SUCCEEDED(hr) && !title.empty()) {
221 std::wstring wideTitle(title.begin(), title.end());
222 pfd->SetTitle(wideTitle.c_str());
227 hr = pfd->Show(GetActiveWindow());
232 IShellItem *psi =
nullptr;
233 hr = pfd->GetResult(&psi);
235 PWSTR pszPath =
nullptr;
236 hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
237 if (SUCCEEDED(hr) && pszPath) {
239 const int size = WideCharToMultiByte(CP_UTF8, 0, pszPath, -1,
nullptr, 0,
nullptr,
nullptr);
241 folder_path.resize(size - 1);
242 WideCharToMultiByte(CP_UTF8, 0, pszPath, -1, &folder_path[0], size,
nullptr,
nullptr);
244 CoTaskMemFree(pszPath);
256 std::string
FileDialog::imgui_save_file(
const std::string &default_filename,
257 const std::vector<FileFilter> &filters) {
258 static std::string filepath;
262 std::string filterDesc =
"Save as: ";
263 if (filters.empty()) {
264 filterDesc +=
"All files";
266 for (size_t i = 0; i < filters.size(); ++i) {
267 filterDesc += filters[i].name;
268 if (i < filters.size() - 1) {
274 ImGui::OpenPopup(
"Save File");
276 if (ImGui::BeginPopupModal(
"Save File",
nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
277 static char buf[512] =
"";
280 if (buf[0] ==
'\0' && !default_filename.empty()) {
281 strncpy(buf, default_filename.c_str(),
sizeof(buf) - 1);
284 ImGui::Text(
"%s", filterDesc.c_str());
285 ImGui::InputText(
"Filename", buf, 512);
287 if (ImGui::Button(
"Save", ImVec2(120, 0))) {
291 if (!filters.empty() && filepath.find(
'.') == std::string::npos) {
292 const auto &firstFilter = filters[0];
293 size_t pos = firstFilter.extensions.find(
"*.");
294 if (pos != std::string::npos) {
295 size_t endPos = firstFilter.extensions.find(
';', pos);
296 if (endPos == std::string::npos) {
297 endPos = firstFilter.extensions.size();
299 std::string ext = firstFilter.extensions.substr(pos + 1, endPos - pos - 1);
304 ImGui::CloseCurrentPopup();
309 if (ImGui::Button(
"Cancel", ImVec2(120, 0))) {
310 ImGui::CloseCurrentPopup();
320 std::string
FileDialog::open_file(
const std::vector<FileFilter> &filters,
321 const std::filesystem::path &default_path) {
323 return win32_open_file(filters, default_path);
331 return win32_select_folder(title);
333 return imgui_select_folder(title);
337 std::string
FileDialog::save_file(std::string &save_name_to,
const std::string &default_filename,
338 const std::vector<FileFilter> &filters,
339 const std::filesystem::path &default_path) {
341 return win32_save_file(default_filename, filters, save_name_to, default_path);
static std::string imgui_open_file(const std::vector< FileFilter > &filters)
static std::string select_folder(const std::string &title)
static std::string imgui_save_file(const std::string &default_filename, const std::vector< FileFilter > &filters)
static std::string win32_select_folder(const std::string &title)