1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-04 13:42:55 +01:00

Refactor file / directory dialogs to UiContext

This commit is contained in:
Ted John
2017-04-01 17:27:39 +01:00
committed by Gymnasiast
parent 3fcd42fe2b
commit cf0dcb4753
8 changed files with 348 additions and 366 deletions

View File

@@ -16,11 +16,14 @@
#ifdef __linux__
#include <sstream>
#include <openrct2/common.h>
#include <SDL.h>
#include <openrct2/core/String.hpp>
#include <openrct2/ui/UiContext.h>
#include "UiContext.h"
#include <SDL.h>
namespace OpenRCT2 { namespace Ui
{
enum class DIALOG_TYPE
@@ -107,6 +110,100 @@ namespace OpenRCT2 { namespace Ui
}
}
std::string ShowFileDialog(SDL_Window * window, const FileDialogDesc &desc) override
{
std::string result;
std::string executablePath;
DIALOG_TYPE dtype = GetDialogApp(&executablePath);
switch (dtype) {
case DIALOG_TYPE::KDIALOG:
{
std::string action =
(desc.Type == FILE_DIALOG_TYPE::OPEN) ? "--getopenfilename" :
"--getsavefilename";
std::string filter = GetKDialogFilterString(desc.Filters);
std::string cmd = String::StdFormat("%s --title '%s' %s '%s/' ~ '%s'", executablePath, desc.Title, action, desc.InitialDirectory, filter);
std::string output;
if (Execute(cmd, &output) != 0)
{
result = output;
}
break;
}
case DIALOG_TYPE::ZENITY:
{
std::string action = "--file-selection";
std::string flags;
if (desc.Type == FILE_DIALOG_TYPE::SAVE)
{
flags = "--confirm-overwrite --save";
}
std::string filters = GetZenityFilterString(desc.Filters);
std::string cmd = String::StdFormat("%s %s --filename='%s/' %s --title='%s' / %s", executablePath, action, desc.InitialDirectory, flags, desc.Title, filters);
std::string output;
if (Execute(cmd, &output) != 0)
{
result = output;
}
break;
}
default:
log_error("KDialog or Zenity not installed.");
break;
}
log_verbose("filename = %s", result.c_str());
if (desc.Type == FILE_DIALOG_TYPE::OPEN && access(result, F_OK) == -1)
{
std::string msg = String::StdFormat("\"%s\" not found: %s, please choose another file\n", result.c_str(), strerror(errno));
ShowMessageBox(window, msg);
return ShowFileDialog(window, desc);
}
else if (desc.Type == FILE_DIALOG_TYPE::SAVE && access(result, F_OK) != -1 && dtype == DIALOG_TYPE::KDIALOG)
{
std::string cmd = String::StdFormat("%s --yesno \"Overwrite %s?\"", executablePath, result.c_str());
if (Execute(cmd) != 0)
{
result = std::string();
}
}
return result;
}
std::string ShowDirectoryDialog(SDL_Window * window, const std::string &title) override
{
std::string result;
std::string executablePath;
DIALOG_TYPE dtype = GetDialogApp(&executablePath);
switch (dtype) {
case DIALOG_TYPE::KDIALOG:
{
std::string output;
std::string cmd = String::Format("%s --title '%s' --getexistingdirectory /", executablePath.c_str(), title);
if (Execute(cmd, &output) == 0)
{
result = output;
}
break;
}
case DIALOG_TYPE::ZENITY:
{
std::string output;
std::string cmd = String::Format("%s --title='%s' --file-selection --directory /", executablePath.c_str(), title);
if (Execute(cmd, &output) == 0)
{
result = output;
}
break;
}
default:
log_error("KDialog or Zenity not installed.");
break;
}
return result;
}
private:
static DIALOG_TYPE GetDialogApp(std::string * executablePath)
{
@@ -168,6 +265,66 @@ namespace OpenRCT2 { namespace Ui
// Return exit code
return pclose(fpipe);
}
static std::string GetKDialogFilterString(const std::vector<FileDialogDesc::Filter> filters)
{
std::stringstream filtersb;
bool first = true;
for (const auto &filter : filters)
{
// KDialog wants filters space-delimited and we don't expect ';' anywhere else
std::string pattern = filter.Pattern;
for (sint32 i = 0; i < pattern.size(); i++)
{
if (pattern[i] == ';')
{
pattern[i] = ' ';
}
}
if (first)
{
filtersb << String::StdFormat("%s | %s", pattern.c_str(), filter.Name.c_str());
first = false;
}
else
{
filtersb << String::StdFormat("\\n%s | %s", pattern.c_str(), filter.Name.c_str());
}
}
return filtersb.str();
}
static std::string GetZenityFilterString(const std::vector<FileDialogDesc::Filter> filters)
{
// Zenity seems to be case sensitive, while KDialog isn't
std::stringstream filtersb;
bool first = true;
for (const auto &filter : filters)
{
filtersb << " --file-filter='" << filter.Name << " | ";
for (char c : filter.Pattern)
{
if (c == ';')
{
filtersb << ' ';
}
else if (isalpha(c))
{
filtersb << '['
<< toupper(c)
<< tolower(c)
<< ']';
}
else
{
filtersb << c;
}
}
filtersb << "'";
}
return filtersb.str();
}
};
IPlatformUiContext * CreatePlatformUiContext()

View File

@@ -16,18 +16,38 @@
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <sstream>
#include <openrct2/common.h>
#include <openrct2/core/Math.hpp>
#include <openrct2/core/Path.hpp>
#include <openrct2/core/String.hpp>
#include <openrct2/ui/UiContext.h>
#include "UiContext.h"
#undef interface
#include <windows.h>
#include <shlobj.h>
#include <SDL.h>
#include <SDL_syswm.h>
#include <openrct2/core/String.hpp>
#include "UiContext.h"
// Native resource IDs
#include "../../../resources/resource.h"
static std::wstring SHGetPathFromIDListLongPath(LPCITEMIDLIST pidl)
{
std::wstring pszPath(MAX_PATH, 0);
while (!SHGetPathFromIDListEx(pidl, &pszPath[0], (DWORD)pszPath.size(), 0))
{
if (pszPath.size() >= SHRT_MAX)
{
// Clearly not succeeding at all, bail
return std::wstring();
}
pszPath.resize(pszPath.size() * 2);
}
return pszPath;
}
namespace OpenRCT2 { namespace Ui
{
class Win32Context : public IPlatformUiContext
@@ -69,6 +89,93 @@ namespace OpenRCT2 { namespace Ui
MessageBoxW(hwnd, messageW.c_str(), L"OpenRCT2", MB_OK);
}
std::string ShowFileDialog(SDL_Window * window, const FileDialogDesc &desc) override
{
std::wstring wcFilename = String::ToUtf16(desc.DefaultFilename);
wcFilename.resize(Math::Max<size_t>(wcFilename.size(), MAX_PATH));
std::wstring wcTitle = String::ToUtf16(desc.Title);
std::wstring wcInitialDirectory = String::ToUtf16(desc.InitialDirectory);
std::wstring wcFilters = GetFilterString(desc.Filters);
// Set open file name options
OPENFILENAMEW openFileName = { 0 };
openFileName.lStructSize = sizeof(OPENFILENAMEW);
openFileName.hwndOwner = GetHWND(window);
openFileName.lpstrTitle = wcTitle.c_str();
openFileName.lpstrInitialDir = wcInitialDirectory.c_str();
openFileName.lpstrFilter = wcFilters.c_str();
openFileName.lpstrFile = &wcFilename[0];
openFileName.nMaxFile = (DWORD)wcFilename.size();
// Open dialog
BOOL dialogResult = FALSE;
DWORD commonFlags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
if (desc.Type == FILE_DIALOG_TYPE::OPEN)
{
openFileName.Flags = commonFlags | OFN_NONETWORKBUTTON | OFN_FILEMUSTEXIST;
dialogResult = GetOpenFileNameW(&openFileName);
}
else if (desc.Type == FILE_DIALOG_TYPE::SAVE)
{
openFileName.Flags = commonFlags | OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
dialogResult = GetSaveFileNameW(&openFileName);
}
std::string resultFilename;
if (dialogResult)
{
resultFilename = String::ToUtf8(openFileName.lpstrFile);
// If there is no extension, append the pattern
std::string resultExtension = Path::GetExtension(resultFilename);
if (resultExtension.empty())
{
sint32 filterIndex = openFileName.nFilterIndex - 1;
assert(filterIndex >= 0);
assert(filterIndex < desc.Filters.size());
std::string pattern = desc.Filters[filterIndex].Pattern;
std::string patternExtension = Path::GetExtension(pattern);
if (!patternExtension.empty())
{
resultFilename += patternExtension;
}
}
}
return resultFilename;
}
std::string ShowDirectoryDialog(SDL_Window * window, const std::string &title) override
{
std::string result;
// Initialize COM and get a pointer to the shell memory allocator
LPMALLOC lpMalloc;
if (SUCCEEDED(CoInitializeEx(0, COINIT_APARTMENTTHREADED)) &&
SUCCEEDED(SHGetMalloc(&lpMalloc)))
{
std::wstring titleW = String::ToUtf16(title);
BROWSEINFOW bi = { 0 };
bi.hwndOwner = GetHWND(window);
bi.lpszTitle = titleW.c_str();
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE | BIF_NONEWFOLDERBUTTON;
LPITEMIDLIST pidl = SHBrowseForFolderW(&bi);
if (pidl != nullptr)
{
result = String::ToUtf8(SHGetPathFromIDListLongPath(pidl));
}
}
else
{
log_error("Error opening directory browse window");
}
CoUninitialize();
return result;
}
private:
HWND GetHWND(SDL_Window * window)
{
@@ -96,6 +203,19 @@ namespace OpenRCT2 { namespace Ui
}
return result;
}
static std::wstring GetFilterString(const std::vector<FileDialogDesc::Filter> filters)
{
std::wstringstream filtersb;
for (auto filter : filters)
{
filtersb << String::ToUtf16(filter.Name)
<< '\0'
<< String::ToUtf16(filter.Pattern)
<< '\0';
}
return filtersb.str();
}
};
IPlatformUiContext * CreatePlatformUiContext()

View File

@@ -521,6 +521,16 @@ public:
_platformUiContext->ShowMessageBox(_window, message);
}
std::string ShowFileDialog(const FileDialogDesc &desc) override
{
return _platformUiContext->ShowFileDialog(_window, desc);
}
std::string ShowDirectoryDialog(const std::string &title) override
{
return _platformUiContext->ShowDirectoryDialog(_window, title);
}
private:
void OnResize(sint32 width, sint32 height)
{

View File

@@ -27,14 +27,18 @@ namespace OpenRCT2
namespace Ui
{
interface IUiContext;
struct FileDialogDesc;
interface IUiContext;
interface IPlatformUiContext
{
virtual ~IPlatformUiContext() = default;
virtual void SetWindowIcon(SDL_Window * window) abstract;
virtual bool IsSteamOverlayAttached() abstract;
virtual void ShowMessageBox(SDL_Window * window, const std::string &message) abstract;
virtual void ShowMessageBox(SDL_Window * window, const std::string &message) abstract;
virtual std::string ShowFileDialog(SDL_Window * window, const FileDialogDesc &desc) abstract;
virtual std::string ShowDirectoryDialog(SDL_Window * window, const std::string &title) abstract;
};
IUiContext * CreateUiContext();

View File

@@ -680,4 +680,29 @@ extern "C"
{
GetContext()->GetUiContext()->SetCursorTrap(value);
}
bool platform_open_common_file_dialog(utf8 * outFilename, file_dialog_desc * desc, size_t outSize)
{
FileDialogDesc desc2;
desc2.Type = (FILE_DIALOG_TYPE)desc->type;
desc2.Title = String::ToStd(desc->title);
desc2.InitialDirectory = String::ToStd(desc->initial_directory);
desc2.DefaultFilename = String::ToStd(desc->default_filename);
for (const auto &filter : desc->filters)
{
if (filter.name != nullptr)
{
desc2.Filters.push_back({ String::ToStd(filter.name), String::ToStd(filter.pattern) });
}
}
std::string result = GetContext()->GetUiContext()->ShowFileDialog(desc2);
String::Set(outFilename, outSize, result.c_str());
return !result.empty();
}
utf8 * platform_open_directory_browser(const utf8 * title)
{
std::string result = GetContext()->GetUiContext()->ShowDirectoryDialog(title);
return String::Duplicate(result.c_str());
}
}

View File

@@ -39,8 +39,6 @@
#include "../util/util.h"
#include "platform.h"
typedef enum { DT_NONE, DT_KDIALOG, DT_ZENITY } dialog_type;
void platform_get_exe_path(utf8 *outPath, size_t outSize)
{
char exePath[MAX_PATH];
@@ -222,217 +220,6 @@ uint8 platform_get_locale_measurement_format(){
return MEASUREMENT_FORMAT_METRIC;
}
static void execute_cmd(char *command, sint32 *exit_value, char *buf, size_t *buf_size) {
FILE *f;
size_t n_chars;
log_verbose("executing \"%s\"...\n", command);
f = popen(command, "r");
if (buf && buf_size) {
n_chars = fread(buf, 1, *buf_size, f);
// some commands may return a new-line terminated result, trim that
if (n_chars > 0 && buf[n_chars - 1] == '\n') {
buf[n_chars - 1] = '\0';
}
// make sure string is null-terminated
if (n_chars == *buf_size) {
n_chars--;
}
buf[n_chars] = '\0';
// account for null terminator
*buf_size = n_chars + 1;
} else {
fflush(f);
}
if (exit_value)
*exit_value = pclose(f);
else
pclose(f);
}
bool platform_open_common_file_dialog(utf8 *outFilename, file_dialog_desc *desc, size_t outSize) {
sint32 exit_value;
char executable[MAX_PATH];
char cmd[OPENRCT2_MAX_COMMAND_LENGTH];
char result[OPENRCT2_MAX_COMMAND_LENGTH];
size_t size;
dialog_type dtype;
char *action = NULL;
char *flags = NULL;
char filter[OPENRCT2_MAX_COMMAND_LENGTH] = { 0 };
char filterPatternRegex[64];
size = OPENRCT2_MAX_COMMAND_LENGTH;
dtype = get_dialog_app(executable, &size);
switch (dtype) {
case DT_KDIALOG:
switch (desc->type) {
case FD_OPEN:
action = "--getopenfilename";
break;
case FD_SAVE:
action = "--getsavefilename";
break;
}
{
bool first = true;
for (sint32 j = 0; j < countof(desc->filters); j++) {
if (desc->filters[j].pattern && desc->filters[j].name) {
char filterTemp[100] = { 0 };
if (first) {
snprintf(filterTemp, countof(filterTemp), "%s | %s", desc->filters[j].pattern, desc->filters[j].name);
first = false;
} else {
snprintf(filterTemp, countof(filterTemp), "\\n%s | %s", desc->filters[j].pattern, desc->filters[j].name);
}
safe_strcat(filter, filterTemp, countof(filter));
}
}
char filterTemp[100] = { 0 };
if (first) {
snprintf(filterTemp, countof(filterTemp), "*|%s", (char *)language_get_string(STR_ALL_FILES));
} else {
snprintf(filterTemp, countof(filterTemp), "\\n*|%s", (char *)language_get_string(STR_ALL_FILES));
}
safe_strcat(filter, filterTemp, countof(filter));
// kdialog wants filters space-delimited and we don't expect ';' anywhere else,
// this is much easier and quicker to do than being overly careful about replacing
// it only where truly needed.
sint32 filterSize = strlen(filter);
for (sint32 i = 0; i < filterSize + 3; i++) {
if (filter[i] == ';') {
filter[i] = ' ';
}
}
}
snprintf(cmd, OPENRCT2_MAX_COMMAND_LENGTH, "%s --title '%s' %s '%s/' ~ '%s'", executable, desc->title, action, desc->initial_directory, filter);
break;
case DT_ZENITY:
action = "--file-selection";
switch (desc->type) {
case FD_SAVE:
flags = "--confirm-overwrite --save";
break;
case FD_OPEN:
flags = "";
break;
}
// Zenity seems to be case sensitive, while Kdialog isn't.
for (sint32 j = 0; j < countof(desc->filters); j++) {
if (desc->filters[j].pattern && desc->filters[j].name) {
sint32 regexIterator = 0;
for(sint32 i = 0; i <= strlen(desc->filters[j].pattern); i++) {
if (isalpha(desc->filters[j].pattern[i])) {
filterPatternRegex[regexIterator+0] = '[';
filterPatternRegex[regexIterator+1] = (char)toupper(desc->filters[j].pattern[i]);
filterPatternRegex[regexIterator+2] = (char)tolower(desc->filters[j].pattern[i]);
filterPatternRegex[regexIterator+3] = ']';
regexIterator += 3;
}
else if(desc->filters[j].pattern[i] == ';') {
filterPatternRegex[regexIterator] = ' ';
}
else {
filterPatternRegex[regexIterator] = (char)desc->filters[j].pattern[i];
}
regexIterator++;
}
filterPatternRegex[regexIterator+1] = 0;
char filterTemp[100] = { 0 };
snprintf(filterTemp, countof(filterTemp), " --file-filter='%s | %s'", desc->filters[j].name, filterPatternRegex);
safe_strcat(filter, filterTemp, countof(filter));
}
}
char filterTemp[100] = { 0 };
snprintf(filterTemp, countof(filterTemp), " --file-filter='%s | *'", (char *)language_get_string(STR_ALL_FILES));
safe_strcat(filter, filterTemp, countof(filter));
snprintf(cmd, OPENRCT2_MAX_COMMAND_LENGTH, "%s %s --filename='%s/' %s --title='%s' / %s", executable, action, desc->initial_directory, flags, desc->title, filter);
break;
default: return 0;
}
size = OPENRCT2_MAX_COMMAND_LENGTH;
execute_cmd(cmd, &exit_value, result, &size);
if (exit_value != 0) {
return 0;
}
result[size-1] = '\0';
log_verbose("filename = %s", result);
if (desc->type == FD_OPEN && access(result, F_OK) == -1) {
char msg[OPENRCT2_MAX_COMMAND_LENGTH];
snprintf(msg, OPENRCT2_MAX_COMMAND_LENGTH, "\"%s\" not found: %s, please choose another file\n", result, strerror(errno));
platform_show_messagebox(msg);
return platform_open_common_file_dialog(outFilename, desc, outSize);
} else
if (desc->type == FD_SAVE && access(result, F_OK) != -1 && dtype == DT_KDIALOG) {
snprintf(cmd, OPENRCT2_MAX_COMMAND_LENGTH, "%s --yesno \"Overwrite %s?\"", executable, result);
size = OPENRCT2_MAX_COMMAND_LENGTH;
execute_cmd(cmd, &exit_value, 0, 0);
if (exit_value != 0) {
return 0;
}
}
safe_strcpy(outFilename, result, outSize);
return 1;
}
utf8 *platform_open_directory_browser(const utf8 *title) {
size_t size;
dialog_type dtype;
sint32 exit_value;
char cmd[OPENRCT2_MAX_COMMAND_LENGTH];
char executable[MAX_PATH];
char result[OPENRCT2_MAX_COMMAND_LENGTH];
char *return_value;
size = MAX_PATH;
dtype = get_dialog_app(executable, &size);
switch (dtype) {
case DT_KDIALOG:
snprintf(cmd, OPENRCT2_MAX_COMMAND_LENGTH, "%s --title '%s' --getexistingdirectory /", executable, title);
break;
case DT_ZENITY:
snprintf(cmd, OPENRCT2_MAX_COMMAND_LENGTH, "%s --title='%s' --file-selection --directory /", executable, title);
break;
default: return 0;
}
size = OPENRCT2_MAX_COMMAND_LENGTH;
execute_cmd(cmd, &exit_value, result, &size);
if (exit_value != 0) {
return NULL;
}
assert(size - 1 < countof(result));
result[size-1] = '\0';
return_value = _strdup(result);
return return_value;
}
#ifndef NO_TTF
bool platform_get_font_path(TTFFontDescriptor *font, utf8 *buffer, size_t size)
{

View File

@@ -584,151 +584,6 @@ void platform_get_user_directory(utf8 *outPath, const utf8 *subDirectory, size_t
}
}
/**
*
* rct2: 0x004080EA
*/
bool platform_open_common_file_dialog(utf8 *outFilename, file_dialog_desc *desc, size_t outSize)
{
OPENFILENAMEW openFileName;
wchar_t wcFilename[MAX_PATH];
// Copy default filename to result filename buffer
if (desc->default_filename == NULL) {
wcFilename[0] = 0;
} else {
wchar_t *wcDefaultFilename = utf8_to_widechar(desc->default_filename);
lstrcpyW(wcFilename, wcDefaultFilename);
free(wcDefaultFilename);
}
// Set open file name options
memset(&openFileName, 0, sizeof(OPENFILENAMEW));
openFileName.lStructSize = sizeof(OPENFILENAMEW);
openFileName.hwndOwner = NULL; // windows_get_window_handle();
openFileName.nMaxFile = MAX_PATH;
openFileName.lpstrTitle = utf8_to_widechar(desc->title);
openFileName.lpstrInitialDir = utf8_to_widechar(desc->initial_directory);
openFileName.lpstrFile = wcFilename;
utf8 filters[256];
utf8 *ch = filters;
for (sint32 i = 0; i < countof(desc->filters); i++) {
if (desc->filters[i].name != NULL) {
safe_strcpy(ch, desc->filters[i].name, sizeof(filters) - (ch - filters));
ch = strchr(ch, 0) + 1;
safe_strcpy(ch, desc->filters[i].pattern, sizeof(filters) - (ch - filters));
ch = strchr(ch, 0) + 1;
}
}
assert(ch != filters);
*ch = 0;
// HACK: Replace all null terminators with 0x01 so that we convert the entire string
size_t fullLength = (size_t)(ch - filters);
for (size_t i = 0; i < fullLength; i++) {
if (filters[i] == '\0') {
filters[i] = 1;
}
}
wchar_t *wcFilter = utf8_to_widechar(filters);
fullLength = lstrlenW(wcFilter);
for (size_t i = 0; i < fullLength; i++) {
if (wcFilter[i] == 1) {
wcFilter[i] = '\0';
}
}
openFileName.lpstrFilter = wcFilter;
// Open dialog
BOOL result = false;
DWORD commonFlags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
if (desc->type == FD_OPEN) {
openFileName.Flags = commonFlags | OFN_NONETWORKBUTTON | OFN_FILEMUSTEXIST;
result = GetOpenFileNameW(&openFileName);
} else if (desc->type == FD_SAVE) {
openFileName.Flags = commonFlags | OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
result = GetSaveFileNameW(&openFileName);
}
// Clean up
free((void*)openFileName.lpstrTitle);
free((void*)openFileName.lpstrInitialDir);
free((void*)openFileName.lpstrFilter);
if (result) {
utf8 *resultFilename = widechar_to_utf8(openFileName.lpstrFile);
safe_strcpy(outFilename, resultFilename, outSize);
free(resultFilename);
// If there is no extension, append the pattern
const utf8 *outFilenameExtension = path_get_extension(outFilename);
if (str_is_null_or_empty(outFilenameExtension)) {
sint32 filterIndex = openFileName.nFilterIndex - 1;
assert(filterIndex >= 0);
assert(filterIndex < countof(desc->filters));
const utf8 *pattern = desc->filters[filterIndex].pattern;
const utf8 *patternExtension = path_get_extension(pattern);
if (!str_is_null_or_empty(patternExtension)) {
safe_strcat(outFilename, patternExtension, outSize);
}
}
}
return result;
}
utf8 *platform_open_directory_browser(const utf8 *title)
{
BROWSEINFOW bi;
wchar_t pszBuffer[MAX_PATH], wctitle[256];
LPITEMIDLIST pidl;
LPMALLOC lpMalloc;
MultiByteToWideChar(CP_UTF8, 0, title, -1, wctitle, countof(wctitle));
// Initialize COM
if (FAILED(CoInitializeEx(0, COINIT_APARTMENTTHREADED))) {
CoUninitialize();
log_error("Error opening directory browse window");
return 0;
}
// Get a pointer to the shell memory allocator
if (FAILED(SHGetMalloc(&lpMalloc))) {
CoUninitialize();
log_error("Error opening directory browse window");
return 0;
}
bi.hwndOwner = NULL;
bi.pidlRoot = NULL;
bi.pszDisplayName = pszBuffer;
bi.lpszTitle = wctitle;
bi.ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
bi.lpfn = NULL;
bi.lParam = 0;
utf8 *outPath = NULL;
if ((pidl = SHBrowseForFolderW(&bi)) != NULL) {
// Copy the path directory to the buffer
if (SHGetPathFromIDListW(pidl, pszBuffer)) {
// Store pszBuffer (and the path) in the outPath
sint32 outPathCapacity = lstrlenW(pszBuffer) * 4 + 1;
outPath = (utf8*)malloc(outPathCapacity);
WideCharToMultiByte(CP_UTF8, 0, pszBuffer, countof(pszBuffer), outPath, outPathCapacity, NULL, NULL);
}
}
CoUninitialize();
return outPath;
}
/**
*
* rct2: 0x00407978

View File

@@ -62,6 +62,27 @@ namespace OpenRCT2
return !(lhs == rhs);
}
enum class FILE_DIALOG_TYPE
{
OPEN,
SAVE,
};
struct FileDialogDesc
{
struct Filter
{
std::string Name; // E.g. "Image Files"
std::string Pattern; // E.g. "*.png;*.jpg;*.gif"
};
FILE_DIALOG_TYPE Type = FILE_DIALOG_TYPE::OPEN;
std::string Title;
std::string InitialDirectory;
std::string DefaultFilename;
std::vector<Filter> Filters;
};
/**
* Represents the window or screen that OpenRCT2 is presented on.
*/
@@ -82,7 +103,10 @@ namespace OpenRCT2
virtual bool IsSteamOverlayActive() abstract;
virtual void ProcessMessages() abstract;
virtual void TriggerResize() abstract;
virtual void ShowMessageBox(const std::string &message) abstract;
virtual void ShowMessageBox(const std::string &message) abstract;
virtual std::string ShowFileDialog(const FileDialogDesc &desc) abstract;
virtual std::string ShowDirectoryDialog(const std::string &title) abstract;
// Input
virtual const CursorState * GetCursorState() abstract;