basically finish dialog helper
This commit is contained in:
parent
c9152bffa8
commit
7258c4c92b
@ -0,0 +1,162 @@
|
|||||||
|
#include "DialogHelper.hpp"
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
|
||||||
|
#include "EncodingHelper.hpp"
|
||||||
|
|
||||||
|
namespace YYCC::DialogHelper {
|
||||||
|
|
||||||
|
template<bool TIsOpen, bool TMultiSelection>
|
||||||
|
bool GeneralFileDialog(const FileDialogParameter& params, std::vector<std::string>& ret) {
|
||||||
|
// make sure multi-selection only available in open mode
|
||||||
|
static_assert(TIsOpen && (!TIsOpen && TMultiSelection == false));
|
||||||
|
|
||||||
|
// build filter
|
||||||
|
std::wstring w_filter;
|
||||||
|
for (const auto& filter_pair : params.m_Filter) {
|
||||||
|
w_filter += EncodingHelper::UTF8ToWchar(filter_pair.first.c_str());
|
||||||
|
w_filter += L'\0';
|
||||||
|
w_filter += EncodingHelper::UTF8ToWchar(filter_pair.second.c_str());
|
||||||
|
w_filter += L'\0';
|
||||||
|
}
|
||||||
|
// build title
|
||||||
|
std::wstring w_title(EncodingHelper::UTF8ToWchar(params.m_Title.c_str()));
|
||||||
|
// build default extension
|
||||||
|
std::wstring w_default_ext(EncodingHelper::UTF8ToWchar(params.m_DefaultExtension.c_str()));
|
||||||
|
// build initial directory
|
||||||
|
std::wstring w_init_dir(EncodingHelper::UTF8ToWchar(params.m_InitialDirectory.c_str()));
|
||||||
|
// prepare file name receiver and preset it as initial file name
|
||||||
|
std::wstring path_receiver(EncodingHelper::UTF8ToWchar(params.m_InitialFileName.c_str()));
|
||||||
|
path_receiver.resize(std::max(MAX_PATH, path_receiver.size()), L'\0');
|
||||||
|
|
||||||
|
// prepare the common part of file dialog struct
|
||||||
|
OPENFILENAMEW dialog_param;
|
||||||
|
ZeroMemory(&dialog_param, sizeof(OPENFILENAMEW));
|
||||||
|
dialog_param.lStructSize = sizeof(OPENFILENAMEW);
|
||||||
|
// dialog owner
|
||||||
|
dialog_param.hwndOwner = params.m_Owner;
|
||||||
|
// if no filter, we pass NULL
|
||||||
|
dialog_param.lpstrFilter = w_filter.empty() ? NULL : w_filter.c_str();
|
||||||
|
// no record to user selected filter
|
||||||
|
dialog_param.lpstrCustomFilter = NULL;
|
||||||
|
dialog_param.nMaxCustFilter = 0;
|
||||||
|
dialog_param.nFilterIndex = 0;
|
||||||
|
// path receiver, also is init filename
|
||||||
|
dialog_param.lpstrFile = path_receiver.data();
|
||||||
|
dialog_param.nMaxFile = static_caast<DWORD>(path_receiver.size());
|
||||||
|
// no selected file infos
|
||||||
|
dialog_param.lpstrFileTitle = NULL;
|
||||||
|
dialog_param.nMaxFileTitle = 0;
|
||||||
|
// initial directory
|
||||||
|
dialog_param.lpstrInitialDir = w_init_dir.empty() ? NULL : w_init_dir.c_str();
|
||||||
|
// dialog title
|
||||||
|
dialog_param.lpstrTitle = w_title.empty() ? NULL : w_title.c_str();
|
||||||
|
// setup basic flags
|
||||||
|
dialog_param.Flags = OFN_EXPLORER;
|
||||||
|
// default extension
|
||||||
|
dialog_param.lpstrDefExt = w_default_ext.empty() ? NULL : w_default_ext.c_str();
|
||||||
|
|
||||||
|
BOOL status;
|
||||||
|
if constexpr (TIsOpen) {
|
||||||
|
// multi-selection need add special multi-selection flags
|
||||||
|
if constexpr (TMultiSelection) {
|
||||||
|
dialog_param.Flags |= OFN_ALLOWMULTISELECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call browser
|
||||||
|
status = GetOpenFileNameW(&dialog_param);
|
||||||
|
|
||||||
|
// only process result when success
|
||||||
|
if (status) {
|
||||||
|
if constexpr (TMultiSelection) {
|
||||||
|
// get directory part, copy from start to dialog param specified offset
|
||||||
|
std::wstring w_directory_part(path_receiver.c_str(), dialog_param.nFileOffset);
|
||||||
|
// get file names part one by one
|
||||||
|
size_t filename_cursor = dialog_param.nFileOffset;
|
||||||
|
while (path_receiver[filename_cursor] != L'\0') {
|
||||||
|
// init wstring from given offset
|
||||||
|
std::wstring w_filename_part(path_receiver.c_str() + filename_cursor);
|
||||||
|
// get eaten chars from result and increase to cursor
|
||||||
|
filename_cursor += w_filename_part.size() + 1u;
|
||||||
|
// combine 2 parts and insert into list
|
||||||
|
ret.emplace_back(u8string(EncodingHelper::WcharToUTF8(w_directory_part + w_filename_part)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret.emplace_back(u8string(EncodingHelper::WcharToUTF8(path_receiver.c_str())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// call browser
|
||||||
|
status = GetSaveFileNameW(&dialog_param);
|
||||||
|
|
||||||
|
// only process result when success
|
||||||
|
if (status) {
|
||||||
|
ret.emplace_back(u8string(EncodingHelper::WcharToUTF8(path_receiver.c_str())));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if failed, clear result
|
||||||
|
// and return result
|
||||||
|
if (!status) {
|
||||||
|
ret.clear();
|
||||||
|
}
|
||||||
|
return status == TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenFileDialog(const FileDialogParameter& params, std::string& ret) {
|
||||||
|
std::vector<std::string> cache;
|
||||||
|
bool isok = GeneralFileDialog<true, false>(params, cache);
|
||||||
|
if (isok) ret = cache.front();
|
||||||
|
return isok;
|
||||||
|
}
|
||||||
|
bool OpenMultipleFileDialog(const FileDialogParameter& params, std::vector<std::string>& ret) {
|
||||||
|
return GeneralFileDialog<true, true>(params, ret);
|
||||||
|
}
|
||||||
|
bool SaveFileDialog(const FileDialogParameter& params, std::string& ret) {
|
||||||
|
std::vector<std::string> cache;
|
||||||
|
bool isok = GeneralFileDialog<false, false>(params, cache);
|
||||||
|
if (isok) ret = cache.front();
|
||||||
|
return isok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenFolderDialog(const FolderDialogParameter& params, std::string& ret) {
|
||||||
|
// create wchar string cache for windows W-tail function
|
||||||
|
std::wstring w_title(EncodingHelper::UTF8ToWchar(params.m_Title.c_str()));
|
||||||
|
// create buffer for receiving selected folder
|
||||||
|
// initialize with MAX_PATH length and content is filled with zero.
|
||||||
|
std::wstring path_receiver(MAX_PATH, L'\0');
|
||||||
|
|
||||||
|
// prepare folder foalog struct
|
||||||
|
BROWSEINFOW dialog_param = { 0 };
|
||||||
|
dialog_param.hwndOwner = params.m_Owner;
|
||||||
|
dialog_param.pidlRoot = nullptr;
|
||||||
|
dialog_param.pszDisplayName = path_receiver.data();
|
||||||
|
dialog_param.lpszTitle = w_title.c_str();
|
||||||
|
dialog_param.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
|
||||||
|
dialog_param.lpfn = nullptr;
|
||||||
|
|
||||||
|
// call browser
|
||||||
|
PIDLIST_ABSOLUTE place = SHBrowseForFolderW(&dialog_param);
|
||||||
|
// if browser failed, return.
|
||||||
|
if (place == nullptr) {
|
||||||
|
ret.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get path from browser result
|
||||||
|
BOOL status;
|
||||||
|
if (status = SHGetPathFromIDListW(place, path_receiver.data())) {
|
||||||
|
EncodingHelper::WcharToUTF8(path_receiver.c_str(), ret);
|
||||||
|
} else {
|
||||||
|
ret.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear browser result and return
|
||||||
|
CoTaskMemFree(place);
|
||||||
|
return status == TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -7,21 +7,42 @@
|
|||||||
|
|
||||||
#include "WinImportPrefix.hpp"
|
#include "WinImportPrefix.hpp"
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#include <shlobj_core.h>
|
||||||
|
#include <commdlg.h>
|
||||||
#include "WinImportSuffix.hpp"
|
#include "WinImportSuffix.hpp"
|
||||||
|
|
||||||
namespace YYCC::DialogHelper {
|
namespace YYCC::DialogHelper {
|
||||||
|
|
||||||
struct FileDialogFilterEntry {
|
struct FileDialogParameter {
|
||||||
std::string FileType;
|
FileDialogParameter() :
|
||||||
std::string FileExtension;
|
m_Owner(nullptr),
|
||||||
|
m_Filter(), m_SelectedFilter(0),
|
||||||
|
m_Title(),
|
||||||
|
m_DefaultExtension(), m_InitialDirectory(), m_InitialFileName() {}
|
||||||
|
|
||||||
|
HWND m_Owner;
|
||||||
|
std::vector<std::pair<u8string, u8string>> m_Filter;
|
||||||
|
size_t m_SelectedFilter;
|
||||||
|
u8string m_Title;
|
||||||
|
u8string m_DefaultExtension;
|
||||||
|
u8string m_InitialFileName;
|
||||||
|
u8string m_InitialDirectory;
|
||||||
};
|
};
|
||||||
using FileDialogFilter = std::vector<FileDialogFilterEntry>;
|
|
||||||
|
|
||||||
bool OpenFileDialog(HWND parent, const char* title, const FileDialogFilter& filter, std::string& ret);
|
struct FolderDialogParameter {
|
||||||
bool OpenMultipleFileDialog(HWND parent, const char* title, const FileDialogFilter& filter, std::vector<std::string>& ret);
|
FolderDialogParameter() :
|
||||||
bool SaveFileDialog(HWND parent, const char* title, const FileDialogFilter& filter, std::string& ret);
|
m_Owner(nullptr),
|
||||||
|
m_Title() {}
|
||||||
|
|
||||||
bool OpenFolderDialog(HWND parent, std::string& ret);
|
HWND m_Owner;
|
||||||
|
u8string m_Title;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenFileDialog(const FileDialogParameter& params, std::string& ret);
|
||||||
|
bool OpenMultipleFileDialog(const FileDialogParameter& params, std::vector<std::string>& ret);
|
||||||
|
bool SaveFileDialog(const FileDialogParameter& params, std::string& ret);
|
||||||
|
|
||||||
|
bool OpenFolderDialog(const FolderDialogParameter& params, std::string& ret);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,79 +3,67 @@
|
|||||||
|
|
||||||
namespace YYCC::EncodingHelper {
|
namespace YYCC::EncodingHelper {
|
||||||
|
|
||||||
bool WcharToChar(const wchar_t* src, std::string& dest, UINT codepage) {
|
bool WcharToChar(const wchar_t* src, u8string& dest, UINT codepage) {
|
||||||
int count, write_result;
|
int count, write_result;
|
||||||
|
|
||||||
//converter to CHAR
|
//converter to CHAR
|
||||||
count = WideCharToMultiByte(codepage, 0, src, -1, NULL, 0, NULL, NULL);
|
count = WideCharToMultiByte(codepage, 0, reinterpret_cast<LPCWCH>(src), -1, NULL, 0, NULL, NULL);
|
||||||
if (count <= 0) return false;
|
if (count <= 0) return false;
|
||||||
|
|
||||||
dest.resize(count - 1);
|
dest.resize(count - 1);
|
||||||
write_result = WideCharToMultiByte(codepage, 0, src, -1, dest.data(), count, NULL, NULL);
|
write_result = WideCharToMultiByte(codepage, 0, reinterpret_cast<LPCWCH>(src), -1, reinterpret_cast<LPSTR>(dest.data()), count, NULL, NULL);
|
||||||
if (write_result <= 0) return false;
|
if (write_result <= 0) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool WcharToChar(const std::wstring& src, std::string& dest, UINT codepage) {
|
bool WcharToUTF8(const wchar_t* src, u8string& dest) {
|
||||||
return WcharToChar(src.c_str(), dest, codepage);
|
return WcharToChar(src, dest, CP_UTF8);
|
||||||
}
|
}
|
||||||
std::string WcharToChar(const wchar_t* src, UINT codepage) {
|
u8string WcharToChar(const wchar_t* src, UINT codepage) {
|
||||||
std::string ret;
|
u8string ret;
|
||||||
if (!WcharToChar(src, ret, codepage)) ret.clear();
|
if (!WcharToChar(src, ret, codepage)) ret.clear();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
std::string WcharToChar(const std::wstring& src, UINT codepage) {
|
u8string WcharToUTF8(const wchar_t* src) {
|
||||||
std::string ret;
|
return WcharToChar(src, CP_UTF8);
|
||||||
if (!WcharToChar(src.c_str(), ret, codepage)) ret.clear();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharToWchar(const char* src, std::wstring& dest, UINT codepage) {
|
bool CharToWchar(const u8char* src, std::wstring& dest, UINT codepage) {
|
||||||
int wcount, write_result;
|
int wcount, write_result;
|
||||||
|
|
||||||
// convert to WCHAR
|
// convert to WCHAR
|
||||||
wcount = MultiByteToWideChar(codepage, 0, src, -1, NULL, 0);
|
wcount = MultiByteToWideChar(codepage, 0, reinterpret_cast<LPCCH>(src), -1, NULL, 0);
|
||||||
if (wcount <= 0) return false;
|
if (wcount <= 0) return false;
|
||||||
|
|
||||||
dest.resize(wcount - 1);
|
dest.resize(wcount - 1);
|
||||||
write_result = MultiByteToWideChar(codepage, 0, src, -1, dest.data(), wcount);
|
write_result = MultiByteToWideChar(codepage, 0, reinterpret_cast<LPCCH>(src), -1, reinterpret_cast<LPWSTR>(dest.data()), wcount);
|
||||||
if (write_result <= 0) return false;
|
if (write_result <= 0) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool CharToWchar(const std::string& src, std::wstring& dest, UINT codepage) {
|
bool UTF8ToWchar(const u8char* src, std::wstring& dest) {
|
||||||
return CharToWchar(src.c_str(), dest, codepage);
|
return CharToWchar(src, dest, CP_UTF8);
|
||||||
}
|
}
|
||||||
std::wstring CharToWchar(const char* src, UINT codepage) {
|
std::wstring CharToWchar(const u8char* src, UINT codepage) {
|
||||||
std::wstring ret;
|
std::wstring ret;
|
||||||
if (!CharToWchar(src, ret, codepage)) ret.clear();
|
if (!CharToWchar(src, ret, codepage)) ret.clear();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
std::wstring CharToWchar(const std::string& src, UINT codepage) {
|
std::wstring UTF8ToWchar(const u8char* src) {
|
||||||
std::wstring ret;
|
return CharToWchar(src, CP_UTF8);
|
||||||
if (!CharToWchar(src.c_str(), ret, codepage)) ret.clear();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharToChar(const char* src, std::string& dest, UINT src_codepage, UINT dest_codepage) {
|
bool CharToChar(const u8char* src, u8string& dest, UINT src_codepage, UINT dest_codepage) {
|
||||||
std::wstring intermediary;
|
std::wstring intermediary;
|
||||||
if (!CharToWchar(src, intermediary, src_codepage)) return false;
|
if (!CharToWchar(src, intermediary, src_codepage)) return false;
|
||||||
if (!WcharToChar(intermediary, dest, dest_codepage)) return false;
|
if (!WcharToChar(intermediary.c_str(), dest, dest_codepage)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool CharToChar(const std::string& src, std::string& dest, UINT src_codepage, UINT dest_codepage) {
|
u8string CharToChar(const u8char* src, UINT src_codepage, UINT dest_codepage) {
|
||||||
return CharToChar(src.c_str(), dest, src_codepage, dest_codepage);
|
u8string ret;
|
||||||
}
|
|
||||||
std::string CharToChar(const char* src, UINT src_codepage, UINT dest_codepage) {
|
|
||||||
std::string ret;
|
|
||||||
if (!CharToChar(src, ret, src_codepage, dest_codepage)) ret.clear();
|
if (!CharToChar(src, ret, src_codepage, dest_codepage)) ret.clear();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
std::string CharToChar(const std::string& src, UINT src_codepage, UINT dest_codepage) {
|
|
||||||
std::string ret;
|
|
||||||
if (!CharToChar(src.c_str(), ret, src_codepage, dest_codepage)) ret.clear();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,20 +10,18 @@
|
|||||||
|
|
||||||
namespace YYCC::EncodingHelper {
|
namespace YYCC::EncodingHelper {
|
||||||
|
|
||||||
bool WcharToChar(const wchar_t* src, std::string& dest, UINT codepage);
|
bool WcharToChar(const wchar_t* src, u8string& dest, UINT codepage);
|
||||||
bool WcharToChar(const std::wstring& src, std::string& dest, UINT codepage);
|
bool WcharToUTF8(const wchar_t* src, u8string& dest);
|
||||||
std::string WcharToChar(const wchar_t* src, UINT codepage);
|
u8string WcharToChar(const wchar_t* src, UINT codepage);
|
||||||
std::string WcharToChar(const std::wstring& src, UINT codepage);
|
u8string WcharToUTF8(const wchar_t* src);
|
||||||
|
|
||||||
bool CharToWchar(const char* src, std::wstring& dest, UINT codepage);
|
bool CharToWchar(const u8char* src, std::wstring& dest, UINT codepage);
|
||||||
bool CharToWchar(const std::string& src, std::wstring& dest, UINT codepage);
|
bool UTF8ToWchar(const u8char* src, std::wstring& dest);
|
||||||
std::wstring CharToWchar(const char* src, UINT codepage);
|
std::wstring CharToWchar(const u8char* src, UINT codepage);
|
||||||
std::wstring CharToWchar(const std::string& src, UINT codepage);
|
std::wstring UTF8ToWchar(const u8char* src);
|
||||||
|
|
||||||
bool CharToChar(const char* src, std::string& dest, UINT src_codepage, UINT dest_codepage);
|
bool CharToChar(const u8char* src, u8string& dest, UINT src_codepage, UINT dest_codepage);
|
||||||
bool CharToChar(const std::string& src, std::string& dest, UINT src_codepage, UINT dest_codepage);
|
u8string CharToChar(const u8char* src, UINT src_codepage, UINT dest_codepage);
|
||||||
std::string CharToChar(const char* src, UINT src_codepage, UINT dest_codepage);
|
|
||||||
std::string CharToChar(const std::string& src, UINT src_codepage, UINT dest_codepage);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,12 +3,9 @@
|
|||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
|
||||||
// Windows also will generate following macros
|
// Define 2 macros to disallow Windows generate MIN and MAX macros
|
||||||
// which may cause the function sign is different in Windows and other platforms.
|
// which cause std::min and std::max can not function as normal.
|
||||||
// So we simply remove them.
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#undef GetObject
|
#define NOMINMAX
|
||||||
#undef GetClassName
|
|
||||||
#undef LoadImage
|
|
||||||
#undef GetTempPath
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -3,9 +3,12 @@
|
|||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
|
||||||
// Define 2 macros to disallow Windows generate MIN and MAX macros
|
// Windows also will generate following macros
|
||||||
// which cause std::min and std::max can not function as normal.
|
// which may cause the function sign is different in Windows and other platforms.
|
||||||
#define WIN32_LEAN_AND_MEAN
|
// So we simply remove them.
|
||||||
#define NOMINMAX
|
#undef GetObject
|
||||||
|
#undef GetClassName
|
||||||
|
#undef LoadImage
|
||||||
|
#undef GetTempPath
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -9,3 +9,15 @@
|
|||||||
#else
|
#else
|
||||||
#define YYCC_OS YYCC_OS_LINUX
|
#define YYCC_OS YYCC_OS_LINUX
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Decide the char type we used
|
||||||
|
#include <string>
|
||||||
|
namespace YYCC {
|
||||||
|
#if defined(__cpp_char8_t)
|
||||||
|
using u8char = char8_t;
|
||||||
|
using u8string = std::u8string
|
||||||
|
#else
|
||||||
|
using u8char = char;
|
||||||
|
using u8string = std::string;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user