diff --git a/src/DialogHelper.cpp b/src/DialogHelper.cpp index 22b323a..8bd66ae 100644 --- a/src/DialogHelper.cpp +++ b/src/DialogHelper.cpp @@ -2,9 +2,81 @@ #if YYCC_OS == YYCC_OS_WINDOWS #include "EncodingHelper.hpp" +#include "StringHelper.hpp" namespace YYCC::DialogHelper { +#pragma region FileFilters + + bool FileFilters::Add(const char* filter_name, std::initializer_list il) { + // assign filter name + if (filter_name == nullptr) return false; + FilterName name(filter_name); + + // assign filter patterns + FilterModes modes; + for (const char* pattern : il) { + if (pattern != nullptr) modes.emplace_back(std::string(pattern)); + } + + // check filter patterns + if (modes.empty()) return false; + + // add into pairs and return + m_Filters.emplace_back(std::make_pair(name, modes)); + return true; + } + + void FileFilters::Clear() { + m_Filters.clear(); + } + + bool FileFilters::Generate(UINT& filter_count, COMDLG_FILTERSPEC*& filter_specs) { + // init defualt value to prevent the scenario that caller do not check return value. + filter_count = 0u; + filter_specs = nullptr; + + // clear win string vector and build new one + m_WinFilters.clear(); + for (const auto& it : m_Filters) { + // convert name to wchar + WinFilterName name; + if (!YYCC::EncodingHelper::UTF8ToWchar(it.first.c_str(), name)) + return false; + + // convert pattern and join them + std::string joined_modes(YYCC::StringHelper::Join(it.second, u8";")); + WinFilterModes modes; + if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes.c_str(), modes)) + return false; + + // append new pair + m_WinFilters.emplace_back(std::make_pair(name, modes)); + } + + // check filter size + // if it overflow the maximum value, return false + size_t count = m_WinFilters.size(); + if (count > std::numeric_limits::max()) + return false; + + // create new win data struct + // and assign string pointer from internal built win string vector. + m_WinDataStruct.reset(new COMDLG_FILTERSPEC[count]); + for (size_t i = 0u; i < count; ++i) { + m_WinDataStruct[i].pszName = m_WinFilters[i].first.c_str(); + m_WinDataStruct[i].pszSpec = m_WinFilters[i].second.c_str(); + } + + // set return value + filter_count = static_cast(count); + filter_specs = m_WinDataStruct.get(); + return true; + } + +#pragma endregion + + //template //bool GeneralFileDialog(const FileDialogParameter& params, std::vector& ret) { // // make sure multi-selection only available in open mode diff --git a/src/DialogHelper.hpp b/src/DialogHelper.hpp index bb29143..850ed24 100644 --- a/src/DialogHelper.hpp +++ b/src/DialogHelper.hpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include "WinImportPrefix.hpp" #include @@ -13,6 +15,57 @@ namespace YYCC::DialogHelper { + /** + * @brief The class represent the file types region in file dialog. + */ + class FileFilters { + public: + FileFilters() : + m_Filters(), + m_WinFilters(), m_WinDataStruct(nullptr) + {} + + /** + * @brief Add a filter pair in file types list. + * @param filter_name[in] The friendly name of the filter. + * @param il[in] A C++ initialize list. + * Every entries must be `const char*` represent a single filter pattern. + * The list at least should have one valid pattern. + * This function will not validate these filter patterns, so please write them carefully. + * @return True if added success, otherwise false. + * @remarks This function allow you register multiple filter patterns for single friendly name. + * For example: `Add("Microsoft Word (*.doc; *.docx)", {"*.doc", "*.docx"})` + */ + bool Add(const char* filter_name, std::initializer_list il); + /** + * @brief Clear filter pairs for following re-use. + */ + void Clear(); + + /** + * @brief Generate Windows dialog system used data struct. + * @param filter_count[out] The count of generated filter data struct. + * @param filter_specs[out] The pointer to generated filter data struct. + * @remarks User should not call this function. This function is used by internal functions. + * @return True if generation is success, otherwise false. + */ + bool Generate(UINT& filter_count, COMDLG_FILTERSPEC*& filter_specs); + + protected: + using FilterModes = std::vector; + using FilterName = std::string; + using FilterPair = std::pair; + + std::vector m_Filters; + + using WinFilterModes = std::wstring; + using WinFilterName = std::wstring; + using WinFilterPair = std::pair; + + std::vector m_WinFilters; + std::unique_ptr m_WinDataStruct; + }; + //struct FileDialogParameter { // FileDialogParameter() : // m_Owner(nullptr), diff --git a/src/YYCCommonplace.hpp b/src/YYCCommonplace.hpp index b4b5fb7..4e2b8f7 100644 --- a/src/YYCCommonplace.hpp +++ b/src/YYCCommonplace.hpp @@ -5,3 +5,4 @@ #include "StringHelper.hpp" #include "EncodingHelper.hpp" #include "TerminalHelper.hpp" +#include "DialogHelper.hpp" diff --git a/testbench/main.cpp b/testbench/main.cpp index 91b0391..3574ce8 100644 --- a/testbench/main.cpp +++ b/testbench/main.cpp @@ -52,9 +52,23 @@ namespace Testbench { } + static void DialogTestbench() { + YYCC::DialogHelper::FileFilters test; + test.Add("Microsoft Word (*.docx; *.doc)", {"*.docx", "*.doc"}); + test.Add("Microsoft Excel (*.xlsx; *.xls)", {"*.xlsx", "*.xls"}); + test.Add("Microsoft PowerPoint (*.pptx; *.ppt)", {"*.pptx", "*.ppt"}); + test.Add("Text File (*.*)", {"*.txt"}); + test.Add("All Files (*.*)", {"*.*"}); + + UINT count; + COMDLG_FILTERSPEC* specs; + bool ret = test.Generate(count, specs); + } + } int main(int argc, char** args) { Testbench::TerminalTestbench(); Testbench::StringTestbench(); + Testbench::DialogTestbench(); }