Files
UnrealEngine/Engine/Source/ThirdParty/MaterialX/MaterialX-1.38.10/source/MaterialXGraphEditor/FileDialog.cpp
2025-05-18 13:04:45 +08:00

234 lines
5.7 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXGraphEditor/FileDialog.h>
#include <MaterialXCore/Exception.h>
#if defined(_WIN32)
#include <windows.h>
#endif
FileDialog::FileDialog(int flags) :
_flags(flags)
{
}
void FileDialog::setTitle(const std::string& title)
{
_title = title;
}
void FileDialog::setTypeFilters(const mx::StringVec& typeFilters)
{
_filetypes.clear();
for (auto typefilter : typeFilters)
{
std::string minusExt = typefilter.substr(1, typefilter.size() - 1);
std::pair<std::string, std::string> filterPair = { minusExt, minusExt };
_filetypes.push_back(filterPair);
}
}
void FileDialog::open()
{
clearSelected();
_openFlag = true;
}
bool FileDialog::isOpened()
{
return _isOpened;
}
bool FileDialog::hasSelected()
{
return !_selectedFilenames.empty();
}
mx::FilePath FileDialog::getSelected()
{
return _selectedFilenames.empty() ? mx::FilePath() : _selectedFilenames[0];
}
void FileDialog::clearSelected()
{
_selectedFilenames.clear();
}
void FileDialog::display()
{
// Only call the dialog if it's not already displayed
if (!_openFlag || _isOpened)
{
return;
}
_openFlag = false;
// Check if we want to save or open
bool save = !(_flags & SelectDirectory) && (_flags & EnterNewFilename);
mx::StringVec result = launchFileDialog(_filetypes, save, false);
std::string path = result.empty() ? "" : result.front();
if (!path.empty())
{
_selectedFilenames.push_back(path);
}
_isOpened = false;
}
#if !defined(__APPLE__)
mx::StringVec launchFileDialog(const std::vector<std::pair<std::string, std::string>>& filetypes, bool save, bool multiple)
{
static const int FILE_DIALOG_MAX_BUFFER = 16384;
if (save && multiple)
{
throw mx::Exception("save and multiple must not both be true.");
}
#if defined(_WIN32)
OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof(OPENFILENAME);
char tmp[FILE_DIALOG_MAX_BUFFER];
ofn.lpstrFile = tmp;
ZeroMemory(tmp, FILE_DIALOG_MAX_BUFFER);
ofn.nMaxFile = FILE_DIALOG_MAX_BUFFER;
ofn.nFilterIndex = 1;
std::string filter;
if (!save && filetypes.size() > 1)
{
filter.append("Supported file types (");
for (size_t i = 0; i < filetypes.size(); ++i)
{
filter.append("*.");
filter.append(filetypes[i].first);
if (i + 1 < filetypes.size())
filter.append(";");
}
filter.append(")");
filter.push_back('\0');
for (size_t i = 0; i < filetypes.size(); ++i)
{
filter.append("*.");
filter.append(filetypes[i].first);
if (i + 1 < filetypes.size())
filter.append(";");
}
filter.push_back('\0');
}
for (auto pair : filetypes)
{
filter.append(pair.second);
filter.append(" (*.");
filter.append(pair.first);
filter.append(")");
filter.push_back('\0');
filter.append("*.");
filter.append(pair.first);
filter.push_back('\0');
}
filter.push_back('\0');
ofn.lpstrFilter = filter.data();
if (save)
{
ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
if (GetSaveFileNameA(&ofn) == FALSE)
return {};
}
else
{
ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if (multiple)
ofn.Flags |= OFN_ALLOWMULTISELECT;
if (GetOpenFileNameA(&ofn) == FALSE)
return {};
}
size_t i = 0;
mx::StringVec result;
while (tmp[i] != '\0')
{
result.emplace_back(&tmp[i]);
i += result.back().size() + 1;
}
if (result.size() > 1)
{
for (i = 1; i < result.size(); ++i)
{
result[i] = result[0] + "\\" + result[i];
}
result.erase(begin(result));
}
if (save && ofn.nFilterIndex > 0)
{
auto ext = filetypes[ofn.nFilterIndex - 1].first;
if (ext != "*")
{
ext.insert(0, ".");
auto& name = result.front();
if (name.size() <= ext.size() ||
name.compare(name.size() - ext.size(), ext.size(), ext) != 0)
{
name.append(ext);
}
}
}
return result;
#else
char buffer[FILE_DIALOG_MAX_BUFFER];
buffer[0] = '\0';
std::string cmd = "zenity --file-selection ";
// The safest separator for multiple selected paths is /, since / can never occur
// in file names. Only where two paths are concatenated will there be two / following
// each other.
if (multiple)
cmd += "--multiple --separator=\"/\" ";
if (save)
cmd += "--save ";
cmd += "--file-filter=\"";
for (auto pair : filetypes)
cmd += "\"*." + pair.first + "\" ";
cmd += "\"";
FILE* output = popen(cmd.c_str(), "r");
if (output == nullptr)
throw mx::Exception("popen() failed -- could not launch zenity!");
while (fgets(buffer, FILE_DIALOG_MAX_BUFFER, output) != NULL)
;
pclose(output);
std::string paths(buffer);
paths.erase(std::remove(paths.begin(), paths.end(), '\n'), paths.end());
mx::StringVec result;
while (!paths.empty())
{
size_t end = paths.find("//");
if (end == std::string::npos)
{
result.emplace_back(paths);
paths = "";
}
else
{
result.emplace_back(paths.substr(0, end));
paths = paths.substr(end + 1);
}
}
return result;
#endif
}
#endif