[wpigui] Update PFD to latest, fix kdialog multiselect (#4005)

This commit is contained in:
Peter Johnson
2022-02-06 00:10:43 -08:00
committed by GitHub
parent 8d08d67cf1
commit 68fe51e8da

View File

@@ -35,6 +35,11 @@
#ifndef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE 2 // for popen()
#endif
#ifdef __APPLE__
# ifndef _DARWIN_C_SOURCE
# define _DARWIN_C_SOURCE
# endif
#endif
#include <cstdio> // popen()
#include <cstdlib> // std::getenv()
#include <fcntl.h> // fcntl()
@@ -50,6 +55,17 @@
#include <thread>
#include <chrono>
// Versions of mingw64 g++ up to 9.3.0 do not have a complete IFileDialog
#ifndef PFD_HAS_IFILEDIALOG
# define PFD_HAS_IFILEDIALOG 1
# if (defined __MINGW64__ || defined __MINGW32__) && defined __GXX_ABI_VERSION
# if __GXX_ABI_VERSION <= 1013
# undef PFD_HAS_IFILEDIALOG
# define PFD_HAS_IFILEDIALOG 0
# endif
# endif
#endif
//
// Below this are all the method implementations.
//
@@ -89,6 +105,18 @@ public:
HMODULE handle;
};
// Helper class around CoInitialize() and CoUnInitialize()
class ole32_dll : public dll
{
public:
ole32_dll();
~ole32_dll();
bool is_initialized();
private:
HRESULT m_state;
};
// Helper class around CreateActCtx() and ActivateActCtx()
class new_style_context
{
@@ -488,6 +516,30 @@ internal::platform::dll::~dll()
}
#endif // _WIN32
// ole32_dll implementation
#if _WIN32
internal::platform::ole32_dll::ole32_dll()
: dll("ole32.dll")
{
// Use COINIT_MULTITHREADED because COINIT_APARTMENTTHREADED causes crashes.
// See https://github.com/samhocevar/portable-file-dialogs/issues/51
auto coinit = proc<HRESULT WINAPI (LPVOID, DWORD)>(*this, "CoInitializeEx");
m_state = coinit(nullptr, COINIT_MULTITHREADED);
}
internal::platform::ole32_dll::~ole32_dll()
{
if (is_initialized())
proc<void WINAPI ()>(*this, "CoUninitialize")();
}
bool internal::platform::ole32_dll::is_initialized()
{
return m_state == S_OK || m_state == S_FALSE;
}
#endif
// new_style_context implementation
#if _WIN32
@@ -637,7 +689,9 @@ class internal::file_dialog::Impl {
public:
#if _WIN32
static int CALLBACK bffcallback(HWND hwnd, UINT uMsg, LPARAM, LPARAM pData);
#if PFD_HAS_IFILEDIALOG
std::string select_folder_vista(IFileDialog *ifd, bool force_path);
#endif
std::wstring m_wtitle;
std::wstring m_wdefault_path;
@@ -671,13 +725,16 @@ internal::file_dialog::file_dialog(type in_type,
m_impl->m_wdefault_path = internal::str2wstr(default_path);
auto wfilter_list = internal::str2wstr(filter_list);
// Initialise COM. This is required for the new folder selection window,
// (see https://github.com/samhocevar/portable-file-dialogs/pull/21)
// and to avoid random crashes with GetOpenFileNameW() (see
// https://github.com/samhocevar/portable-file-dialogs/issues/51)
internal::platform::ole32_dll ole32;
// Folder selection uses a different method
if (in_type == type::folder)
{
internal::platform::dll ole32("ole32.dll");
auto status = internal::platform::dll::proc<HRESULT WINAPI (LPVOID, DWORD)>(ole32, "CoInitializeEx")
(nullptr, COINIT_APARTMENTTHREADED);
#if PFD_HAS_IFILEDIALOG
if (flags(flag::is_vista))
{
// On Vista and higher we should be able to use IFileDialog for folder selection
@@ -689,6 +746,7 @@ internal::file_dialog::file_dialog(type in_type,
if (SUCCEEDED(hr))
return m_impl->select_folder_vista(ifd, options & opt::force_path);
}
#endif
BROWSEINFOW bi;
memset(&bi, 0, sizeof(bi));
@@ -698,9 +756,7 @@ internal::file_dialog::file_dialog(type in_type,
if (flags(flag::is_vista))
{
// This hangs on Windows XP, as reported here:
// https://github.com/samhocevar/portable-file-dialogs/pull/21
if (status == S_OK)
if (ole32.is_initialized())
bi.ulFlags |= BIF_NEWDIALOGSTYLE;
bi.ulFlags |= BIF_EDITBOX;
bi.ulFlags |= BIF_STATUSTEXT;
@@ -716,8 +772,6 @@ internal::file_dialog::file_dialog(type in_type,
ret = internal::wstr2str(buffer);
delete[] buffer;
}
if (status == S_OK)
internal::platform::dll::proc<void WINAPI ()>(ole32, "CoUninitialize")();
return ret;
}
@@ -753,37 +807,36 @@ internal::file_dialog::file_dialog(type in_type,
internal::platform::dll comdlg32("comdlg32.dll");
// Apply new visual style (required for windows XP)
internal::platform::new_style_context ctx;
if (in_type == type::save)
{
if (!(options & opt::force_overwrite))
ofn.Flags |= OFN_OVERWRITEPROMPT;
// using set context to apply new visual style (required for windows XP)
internal::platform::new_style_context ctx;
internal::platform::dll::proc<BOOL WINAPI (LPOPENFILENAMEW)> get_save_file_name(comdlg32, "GetSaveFileNameW");
if (get_save_file_name(&ofn) == 0)
return "";
return internal::wstr2str(woutput.c_str());
}
else
{
if (options & opt::multiselect)
ofn.Flags |= OFN_ALLOWMULTISELECT;
ofn.Flags |= OFN_PATHMUSTEXIST;
if (options & opt::multiselect)
ofn.Flags |= OFN_ALLOWMULTISELECT;
ofn.Flags |= OFN_PATHMUSTEXIST;
// using set context to apply new visual style (required for windows XP)
internal::platform::new_style_context ctx;
internal::platform::dll::proc<BOOL WINAPI (LPOPENFILENAMEW)> get_open_file_name(comdlg32, "GetOpenFileNameW");
if (get_open_file_name(&ofn) == 0)
return "";
internal::platform::dll::proc<BOOL WINAPI (LPOPENFILENAMEW)> get_open_file_name(comdlg32, "GetOpenFileNameW");
if (get_open_file_name(&ofn) == 0)
return "";
}
std::string prefix;
for (wchar_t const *p = woutput.c_str(); *p; )
{
auto filename = internal::wstr2str(p);
p += filename.size();
// In multiselect mode, we advance p one step more and
p += wcslen(p);
// In multiselect mode, we advance p one wchar further and
// check for another filename. If there is one and the
// prefix is empty, it means we just read the prefix.
if ((options & opt::multiselect) && *++p && prefix.empty())
@@ -898,7 +951,10 @@ internal::file_dialog::file_dialog(type in_type,
case type::folder: command.push_back("--getexistingdirectory"); break;
}
if (options & opt::multiselect)
command.push_back(" --multiple");
{
command.push_back("--multiple");
command.push_back("--separate-output");
}
command.push_back(default_path);
@@ -929,8 +985,8 @@ std::string internal::file_dialog::string_result()
auto ret = m_async->result();
// Strip potential trailing newline (zenity). Also strip trailing slash
// added by osascript for consistency with other backends.
while (ret.back() == '\n' || ret.back() == '/')
ret = ret.substr(0, ret.size() - 1);
while (!ret.empty() && (ret.back() == '\n' || ret.back() == '/'))
ret.pop_back();
return ret;
#endif
}
@@ -971,6 +1027,7 @@ int CALLBACK internal::file_dialog::Impl::bffcallback(HWND hwnd, UINT uMsg,
return 0;
}
#if PFD_HAS_IFILEDIALOG
std::string internal::file_dialog::Impl::select_folder_vista(IFileDialog *ifd, bool force_path)
{
std::string result;
@@ -1022,8 +1079,7 @@ std::string internal::file_dialog::Impl::select_folder_vista(IFileDialog *ifd, b
if (wselected)
{
result = internal::wstr2str(std::wstring(wselected));
internal::platform::dll ole32("ole32.dll");
internal::platform::dll::proc<void WINAPI (LPVOID)>(ole32, "CoTaskMemFree")(wselected);
internal::platform::dll::proc<void WINAPI (LPVOID)>(internal::platform::ole32_dll(), "CoTaskMemFree")(wselected);
}
}
}
@@ -1033,6 +1089,7 @@ std::string internal::file_dialog::Impl::select_folder_vista(IFileDialog *ifd, b
return result;
}
#endif
#endif
// notify implementation
@@ -1150,7 +1207,9 @@ message::message(std::string const &title,
icon _icon /* = icon::info */)
{
#if _WIN32
UINT style = MB_TOPMOST;
// Use MB_SYSTEMMODAL rather than MB_TOPMOST to ensure the message window is brought
// to front. See https://github.com/samhocevar/portable-file-dialogs/issues/52
UINT style = MB_SYSTEMMODAL;
switch (_icon)
{
case icon::warning: style |= MB_ICONWARNING; break;
@@ -1177,11 +1236,11 @@ message::message(std::string const &title,
m_mappings[IDRETRY] = button::retry;
m_mappings[IDIGNORE] = button::ignore;
m_async->start_func([this, text, title, style](int* exit_code) -> std::string
m_async->start_func([text, title, style](int* exit_code) -> std::string
{
auto wtext = internal::str2wstr(text);
auto wtitle = internal::str2wstr(title);
// using set context to apply new visual style (required for all windows versions)
// Apply new visual style (required for all Windows versions)
internal::platform::new_style_context ctx;
*exit_code = MessageBoxW(GetActiveWindow(), wtext.c_str(), wtitle.c_str(), style);
return "";