diff --git a/atom/browser/api/lib/dialog.coffee b/atom/browser/api/lib/dialog.coffee index 9f51419dbde7..2bb1e9f721fc 100644 --- a/atom/browser/api/lib/dialog.coffee +++ b/atom/browser/api/lib/dialog.coffee @@ -53,7 +53,7 @@ module.exports = options ?= title: 'Save' options.title ?= '' options.defaultPath ?= '' - options.filter ?= [] + options.filters ?= [] wrappedCallback = if typeof callback is 'function' diff --git a/atom/browser/ui/file_dialog_win.cc b/atom/browser/ui/file_dialog_win.cc index 9a7873347a52..0e4defdece4c 100644 --- a/atom/browser/ui/file_dialog_win.cc +++ b/atom/browser/ui/file_dialog_win.cc @@ -30,103 +30,30 @@ bool IsDirectory(const base::FilePath& path) { file_info.is_directory : path.EndsWithSeparator(); } -// Get the file type description from the registry. This will be "Text Document" -// for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't -// have an entry for the file type, we return false, true if the description was -// found. 'file_ext' must be in form ".txt". -static bool GetRegistryDescriptionFromExtension(const std::wstring& file_ext, - std::wstring* reg_description) { - DCHECK(reg_description); - base::win::RegKey reg_ext(HKEY_CLASSES_ROOT, file_ext.c_str(), KEY_READ); - std::wstring reg_app; - if (reg_ext.ReadValue(NULL, ®_app) == ERROR_SUCCESS && !reg_app.empty()) { - base::win::RegKey reg_link(HKEY_CLASSES_ROOT, reg_app.c_str(), KEY_READ); - if (reg_link.ReadValue(NULL, reg_description) == ERROR_SUCCESS) - return true; - } - return false; -} - -// Set up a filter for a Save/Open dialog, which will consist of |file_ext| file -// extensions (internally separated by semicolons), |ext_desc| as the text -// descriptions of the |file_ext| types (optional), and (optionally) the default -// 'All Files' view. The purpose of the filter is to show only files of a -// particular type in a Windows Save/Open dialog box. The resulting filter is -// returned. The filters created here are: -// 1. only files that have 'file_ext' as their extension -// 2. all files (only added if 'include_all_files' is true) -// Example: -// file_ext: { "*.txt", "*.htm;*.html" } -// ext_desc: { "Text Document" } -// returned: "Text Document\0*.txt\0HTML Document\0*.htm;*.html\0" -// "All Files\0*.*\0\0" (in one big string) -// If a description is not provided for a file extension, it will be retrieved -// from the registry. If the file extension does not exist in the registry, it -// will be omitted from the filter, as it is likely a bogus extension. -void FormatFilterForExtensions( - std::vector* file_ext, - std::vector* ext_desc, - bool include_all_files, - std::vector* file_types) { - DCHECK(file_ext->size() >= ext_desc->size()); - - if (file_ext->empty()) - include_all_files = true; - - for (size_t i = 0; i < file_ext->size(); ++i) { - std::wstring ext = (*file_ext)[i]; - std::wstring desc; - if (i < ext_desc->size()) - desc = (*ext_desc)[i]; - - if (ext.empty()) { - // Force something reasonable to appear in the dialog box if there is no - // extension provided. - include_all_files = true; - continue; - } - - if (desc.empty()) { - DCHECK(ext.find(L'.') != std::wstring::npos); - std::wstring first_extension = ext.substr(ext.find(L'.')); - size_t first_separator_index = first_extension.find(L';'); - if (first_separator_index != std::wstring::npos) - first_extension = first_extension.substr(0, first_separator_index); - - // Find the extension name without the preceeding '.' character. - std::wstring ext_name = first_extension; - size_t ext_index = ext_name.find_first_not_of(L'.'); - if (ext_index != std::wstring::npos) - ext_name = ext_name.substr(ext_index); - - if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) { - // The extension doesn't exist in the registry. Create a description - // based on the unknown extension type (i.e. if the extension is .qqq, - // the we create a description "QQQ File (.qqq)"). - include_all_files = true; - // TODO(zcbenz): should be localized. - desc = base::i18n::ToUpper(base::WideToUTF16(ext_name)) + L" File"; - } - desc += L" (*." + ext_name + L")"; - - // Store the description. - ext_desc->push_back(desc); - } - - COMDLG_FILTERSPEC spec = { (*ext_desc)[i].c_str(), (*file_ext)[i].c_str() }; - file_types->push_back(spec); +void ConvertFilters(const Filters& filters, + std::vector* buffer, + std::vector* filterspec) { + if (filters.empty()) { + COMDLG_FILTERSPEC spec = { L"All Files (*.*)", L"*.*" }; + filterspec->push_back(spec); + return; } - if (include_all_files) { - // TODO(zcbenz): Should be localized. - ext_desc->push_back(L"All Files (*.*)"); - file_ext->push_back(L"*.*"); + buffer->reserve(filters.size() * 2); + for (size_t i = 0; i < filters.size(); ++i) { + const Filter& filter = filters[i]; - COMDLG_FILTERSPEC spec = { - (*ext_desc)[ext_desc->size() - 1].c_str(), - (*file_ext)[file_ext->size() - 1].c_str(), - }; - file_types->push_back(spec); + COMDLG_FILTERSPEC spec; + buffer->push_back(base::UTF8ToWide(filter.first)); + spec.pszName = buffer->back().c_str(); + + std::vector extensions(filter.second); + for (size_t j = 0; j < extensions.size(); ++j) + extensions[j].insert(0, "*."); + buffer->push_back(base::UTF8ToWide(JoinString(extensions, ";"))); + spec.pszSpec = buffer->back().c_str(); + + filterspec->push_back(spec); } } @@ -135,26 +62,18 @@ void FormatFilterForExtensions( template class FileDialog { public: - FileDialog(const base::FilePath& default_path, - const std::string title, - int options, - const std::vector& file_ext, - const std::vector& desc_ext) - : file_ext_(file_ext), - desc_ext_(desc_ext) { - std::vector filters; - FormatFilterForExtensions(&file_ext_, &desc_ext_, true, &filters); - + FileDialog(const base::FilePath& default_path, const std::string& title, + const Filters& filters, int options) { std::wstring file_part; if (!IsDirectory(default_path)) file_part = default_path.BaseName().value(); - dialog_.reset(new T( - file_part.c_str(), - options, - NULL, - filters.data(), - filters.size())); + std::vector buffer; + std::vector filterspec; + ConvertFilters(filters, &buffer, &filterspec); + + dialog_.reset(new T(file_part.c_str(), options, NULL, + filterspec.data(), filterspec.size())); if (!title.empty()) GetPtr()->SetTitle(base::UTF8ToUTF16(title).c_str()); @@ -174,8 +93,6 @@ class FileDialog { IFileDialog* GetPtr() const { return dialog_->GetPtr(); } - const std::vector file_ext() const { return file_ext_; } - private: // Set up the initial directory for the dialog. void SetDefaultFolder(const base::FilePath file_path) { @@ -193,10 +110,6 @@ class FileDialog { scoped_ptr dialog_; - std::vector file_ext_; - std::vector desc_ext_; - std::vector filters_; - DISALLOW_COPY_AND_ASSIGN(FileDialog); }; @@ -215,11 +128,7 @@ bool ShowOpenDialog(atom::NativeWindow* parent_window, options |= FOS_ALLOWMULTISELECT; FileDialog open_dialog( - default_path, - title, - options, - std::vector(), - std::vector()); + default_path, title, filters, options); if (!open_dialog.Show(parent_window)) return false; @@ -263,8 +172,8 @@ void ShowOpenDialog(atom::NativeWindow* parent_window, bool result = ShowOpenDialog(parent_window, title, default_path, - properties, filters, + properties, &paths); callback.Run(result, paths); } @@ -274,42 +183,38 @@ bool ShowSaveDialog(atom::NativeWindow* parent_window, const base::FilePath& default_path, const Filters& filters, base::FilePath* path) { - // TODO(zcbenz): Accept custom filters from caller. - std::vector file_ext; - std::wstring extension = default_path.Extension(); - if (!extension.empty()) - file_ext.push_back(extension.insert(0, L"*")); - FileDialog save_dialog( - default_path, - title, - FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT, - file_ext, - std::vector()); + default_path, title, filters, + FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT); if (!save_dialog.Show(parent_window)) return false; - wchar_t file_name[MAX_PATH]; - HRESULT hr = save_dialog.GetDialog()->GetFilePath(file_name, MAX_PATH); + wchar_t buffer[MAX_PATH]; + HRESULT hr = save_dialog.GetDialog()->GetFilePath(buffer, MAX_PATH); if (FAILED(hr)) return false; + std::string file_name = base::WideToUTF8(std::wstring(buffer)); + // Append extension according to selected filter. - UINT filter_index = 1; - save_dialog.GetPtr()->GetFileTypeIndex(&filter_index); - std::wstring selected_filter = save_dialog.file_ext()[filter_index - 1]; - if (selected_filter != L"*.*") { - std::wstring result = file_name; - if (!EndsWith(result, selected_filter.substr(1), false)) { - if (result[result.length() - 1] != L'.') - result.push_back(L'.'); - result.append(selected_filter.substr(2)); - *path = base::FilePath(result); - return true; + if (!filters.empty()) { + UINT filter_index = 1; + save_dialog.GetPtr()->GetFileTypeIndex(&filter_index); + const Filter& filter = filters[filter_index - 1]; + + bool matched = false; + for (size_t i = 0; i < filter.second.size(); ++i) { + if (EndsWith(file_name, filter.second[i], false)) { + matched = true; + break;; + } } + + if (!matched && !filter.second.empty()) + file_name += ("." + filter.second[0]); } - *path = base::FilePath(file_name); + *path = base::FilePath(base::UTF8ToUTF16(file_name)); return true; }