From f122c44b07be3a670c75689202b20c6e83e23fce Mon Sep 17 00:00:00 2001 From: Zac Walker Date: Wed, 27 Jun 2018 13:09:18 +0200 Subject: [PATCH] Using win32 file open dialogs instead of WTL --- BUILD.gn | 1 - atom/browser/ui/file_dialog_win.cc | 238 ++++++++++++++++------------- 2 files changed, 136 insertions(+), 103 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index fb38e18355ca..f562bb218d3e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -258,7 +258,6 @@ static_library("electron_lib") { } if (is_win) { deps += [ - "//third_party/wtl", "//third_party/breakpad:client", ] } diff --git a/atom/browser/ui/file_dialog_win.cc b/atom/browser/ui/file_dialog_win.cc index 04f832caa258..134cf61fa778 100644 --- a/atom/browser/ui/file_dialog_win.cc +++ b/atom/browser/ui/file_dialog_win.cc @@ -6,12 +6,10 @@ #include // windows.h must be included first -#include // atlbase.h must be included before atlapp.h +#include // atlbase.h for CComPtr -#include -#include -#include #include +#include #include "atom/browser/native_window_views.h" #include "atom/browser/unresponsive_suppressor.h" @@ -24,8 +22,8 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/win/registry.h" -using WTL::CShellFileOpenDialog; -using WTL::CShellFileSaveDialog; +// Used to be linked by WTL +#pragma comment(lib, "gdi32.lib") namespace file_dialog { @@ -69,87 +67,6 @@ void ConvertFilters(const Filters& filters, } } -// Generic class to delegate common open/save dialog's behaviours, users need to -// call interface methods via GetPtr(). -template -class FileDialog { - public: - FileDialog(const DialogSettings& settings, int options) { - std::wstring file_part; - if (!IsDirectory(settings.default_path)) - file_part = settings.default_path.BaseName().value(); - - std::vector buffer; - std::vector filterspec; - ConvertFilters(settings.filters, &buffer, &filterspec); - - dialog_.reset(new T(file_part.c_str(), options, NULL, filterspec.data(), - filterspec.size())); - - if (!settings.title.empty()) - GetPtr()->SetTitle(base::UTF8ToUTF16(settings.title).c_str()); - - if (!settings.button_label.empty()) - GetPtr()->SetOkButtonLabel( - base::UTF8ToUTF16(settings.button_label).c_str()); - - // By default, *.* will be added to the file name if file type is "*.*". In - // Electron, we disable it to make a better experience. - // - // From MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/ - // bb775970(v=vs.85).aspx - // - // If SetDefaultExtension is not called, the dialog will not update - // automatically when user choose a new file type in the file dialog. - // - // We set file extension to the first none-wildcard extension to make - // sure the dialog will update file extension automatically. - for (size_t i = 0; i < filterspec.size(); ++i) { - if (std::wstring(filterspec[i].pszSpec) != L"*.*") { - // SetFileTypeIndex is regarded as one-based index. - GetPtr()->SetFileTypeIndex(i + 1); - GetPtr()->SetDefaultExtension(filterspec[i].pszSpec); - break; - } - } - - if (settings.default_path.IsAbsolute()) { - SetDefaultFolder(settings.default_path); - } - } - - bool Show(atom::NativeWindow* parent_window) { - atom::UnresponsiveSuppressor suppressor; - HWND window = parent_window - ? static_cast(parent_window) - ->GetAcceleratedWidget() - : NULL; - return dialog_->DoModal(window) == IDOK; - } - - T* GetDialog() { return dialog_.get(); } - - IFileDialog* GetPtr() const { return dialog_->GetPtr(); } - - private: - // Set up the initial directory for the dialog. - void SetDefaultFolder(const base::FilePath file_path) { - std::wstring directory = IsDirectory(file_path) - ? file_path.value() - : file_path.DirName().value(); - - ATL::CComPtr folder_item; - HRESULT hr = SHCreateItemFromParsingName(directory.c_str(), NULL, - IID_PPV_ARGS(&folder_item)); - if (SUCCEEDED(hr)) - GetPtr()->SetFolder(folder_item); - } - - std::unique_ptr dialog_; - - DISALLOW_COPY_AND_ASSIGN(FileDialog); -}; - struct RunState { base::Thread* dialog_thread; scoped_refptr ui_task_runner; @@ -189,9 +106,110 @@ void RunSaveDialogInNewThread(const RunState& run_state, } // namespace +static HRESULT GetFileNameFromShellItem(IShellItem* pShellItem, + SIGDN type, + LPWSTR lpstr, + int cchLength) { + assert(pShellItem != NULL); + + LPWSTR lpstrName = NULL; + HRESULT hRet = pShellItem->GetDisplayName(type, &lpstrName); + + if (SUCCEEDED(hRet)) { + if (wcslen(lpstrName) < cchLength) { + wcscpy_s(lpstr, cchLength, lpstrName); + } else { + assert(FALSE); + hRet = DISP_E_BUFFERTOOSMALL; + } + + ::CoTaskMemFree(lpstrName); + } + + return hRet; +} + +static void SetDefaultFolder(IFileDialog* pDialog, + const base::FilePath file_path) { + std::wstring directory = + IsDirectory(file_path) ? file_path.value() : file_path.DirName().value(); + + ATL::CComPtr folder_item; + HRESULT hr = SHCreateItemFromParsingName(directory.c_str(), NULL, + IID_PPV_ARGS(&folder_item)); + if (SUCCEEDED(hr)) + pDialog->SetFolder(folder_item); +} + +static HRESULT ShowFileDialog(IFileDialog* pDialog, + const DialogSettings& settings) { + atom::UnresponsiveSuppressor suppressor; + HWND parent_window = + settings.parent_window + ? static_cast(settings.parent_window) + ->GetAcceleratedWidget() + : NULL; + + return pDialog->Show(parent_window); +} + +static void ApplySettings(IFileDialog* pDialog, + const DialogSettings& settings) { + std::wstring file_part; + + if (!IsDirectory(settings.default_path)) + file_part = settings.default_path.BaseName().value(); + + pDialog->SetFileName(file_part.c_str()); + + if (!settings.title.empty()) + pDialog->SetTitle(base::UTF8ToUTF16(settings.title).c_str()); + + if (!settings.button_label.empty()) + pDialog->SetOkButtonLabel(base::UTF8ToUTF16(settings.button_label).c_str()); + + std::vector buffer; + std::vector filterspec; + ConvertFilters(settings.filters, &buffer, &filterspec); + + if (!filterspec.empty()) { + pDialog->SetFileTypes(filterspec.size(), filterspec.data()); + } + + // By default, *.* will be added to the file name if file type is "*.*". In + // Electron, we disable it to make a better experience. + // + // From MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/ + // bb775970(v=vs.85).aspx + // + // If SetDefaultExtension is not called, the dialog will not update + // automatically when user choose a new file type in the file dialog. + // + // We set file extension to the first none-wildcard extension to make + // sure the dialog will update file extension automatically. + for (size_t i = 0; i < filterspec.size(); ++i) { + if (std::wstring(filterspec[i].pszSpec) != L"*.*") { + // SetFileTypeIndex is regarded as one-based index. + pDialog->SetFileTypeIndex(i + 1); + pDialog->SetDefaultExtension(filterspec[i].pszSpec); + break; + } + } + + if (settings.default_path.IsAbsolute()) { + SetDefaultFolder(pDialog, settings.default_path); + } +} + bool ShowOpenDialog(const DialogSettings& settings, std::vector* paths) { - int options = FOS_FORCEFILESYSTEM | FOS_FILEMUSTEXIST; + ATL::CComPtr pFileOpen; + HRESULT hr = pFileOpen.CoCreateInstance(CLSID_FileOpenDialog); + + if (FAILED(hr)) + return false; + + DWORD options = FOS_FORCEFILESYSTEM | FOS_FILEMUSTEXIST; if (settings.properties & FILE_DIALOG_OPEN_DIRECTORY) options |= FOS_PICKFOLDERS; if (settings.properties & FILE_DIALOG_MULTI_SELECTIONS) @@ -200,14 +218,15 @@ bool ShowOpenDialog(const DialogSettings& settings, options |= FOS_FORCESHOWHIDDEN; if (settings.properties & FILE_DIALOG_PROMPT_TO_CREATE) options |= FOS_CREATEPROMPT; + pFileOpen->SetOptions(options); - FileDialog open_dialog(settings, options); - if (!open_dialog.Show(settings.parent_window)) + ApplySettings(pFileOpen, settings); + hr = ShowFileDialog(pFileOpen, settings); + if (FAILED(hr)) return false; ATL::CComPtr items; - HRESULT hr = - static_cast(open_dialog.GetPtr())->GetResults(&items); + hr = pFileOpen->GetResults(&items); if (FAILED(hr)) return false; @@ -224,8 +243,8 @@ bool ShowOpenDialog(const DialogSettings& settings, return false; wchar_t file_name[MAX_PATH]; - hr = CShellFileOpenDialog::GetFileNameFromShellItem(item, SIGDN_FILESYSPATH, - file_name, MAX_PATH); + hr = GetFileNameFromShellItem(item, SIGDN_FILESYSPATH, file_name, MAX_PATH); + if (FAILED(hr)) return false; @@ -249,17 +268,32 @@ void ShowOpenDialog(const DialogSettings& settings, } bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) { - FileDialog save_dialog( - settings, FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT); - if (!save_dialog.Show(settings.parent_window)) - return false; - - wchar_t buffer[MAX_PATH]; - HRESULT hr = save_dialog.GetDialog()->GetFilePath(buffer, MAX_PATH); + ATL::CComPtr pFileSave; + HRESULT hr = pFileSave.CoCreateInstance(CLSID_FileSaveDialog); if (FAILED(hr)) return false; - *path = base::FilePath(buffer); + pFileSave->SetOptions(FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | + FOS_OVERWRITEPROMPT); + ApplySettings(pFileSave, settings); + hr = ShowFileDialog(pFileSave, settings); + + if (FAILED(hr)) + return false; + + CComPtr pItem; + hr = pFileSave->GetResult(&pItem); + if (FAILED(hr)) + return false; + + PWSTR result_path = nullptr; + hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &result_path); + if (!SUCCEEDED(hr)) + return false; + + *path = base::FilePath(result_path); + CoTaskMemFree(result_path); + return true; }