Merge branch 'master' into no-blocking-browser
This commit is contained in:
		
				commit
				
					
						ac76017702
					
				
			
		
					 1 changed files with 166 additions and 217 deletions
				
			
		|  | @ -4,6 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "browser/ui/file_dialog.h" | #include "browser/ui/file_dialog.h" | ||||||
| 
 | 
 | ||||||
|  | #include <atlbase.h> | ||||||
| #include <windows.h> | #include <windows.h> | ||||||
| #include <commdlg.h> | #include <commdlg.h> | ||||||
| #include <shlobj.h> | #include <shlobj.h> | ||||||
|  | @ -14,19 +15,14 @@ | ||||||
| #include "base/strings/string_split.h" | #include "base/strings/string_split.h" | ||||||
| #include "base/utf_string_conversions.h" | #include "base/utf_string_conversions.h" | ||||||
| #include "base/win/registry.h" | #include "base/win/registry.h" | ||||||
| #include "base/win/windows_version.h" |  | ||||||
| #include "browser/native_window.h" | #include "browser/native_window.h" | ||||||
|  | #include "third_party/wtl/include/atlapp.h" | ||||||
|  | #include "third_party/wtl/include/atldlgs.h" | ||||||
| 
 | 
 | ||||||
| namespace file_dialog { | namespace file_dialog { | ||||||
| 
 | 
 | ||||||
| namespace { | namespace { | ||||||
| 
 | 
 | ||||||
| // Given |extension|, if it's not empty, then remove the leading dot.
 |  | ||||||
| std::wstring GetExtensionWithoutLeadingDot(const std::wstring& extension) { |  | ||||||
|   DCHECK(extension.empty() || extension[0] == L'.'); |  | ||||||
|   return extension.empty() ? extension : extension.substr(1); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Distinguish directories from regular files.
 | // Distinguish directories from regular files.
 | ||||||
| bool IsDirectory(const base::FilePath& path) { | bool IsDirectory(const base::FilePath& path) { | ||||||
|   base::PlatformFileInfo file_info; |   base::PlatformFileInfo file_info; | ||||||
|  | @ -67,26 +63,21 @@ static bool GetRegistryDescriptionFromExtension(const std::wstring& file_ext, | ||||||
| // If a description is not provided for a file extension, it will be retrieved
 | // 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
 | // 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.
 | // will be omitted from the filter, as it is likely a bogus extension.
 | ||||||
| std::wstring FormatFilterForExtensions( | void FormatFilterForExtensions( | ||||||
|     const std::vector<std::wstring>& file_ext, |     std::vector<std::wstring>* file_ext, | ||||||
|     const std::vector<std::wstring>& ext_desc, |     std::vector<std::wstring>* ext_desc, | ||||||
|     bool include_all_files) { |     bool include_all_files, | ||||||
|   const std::wstring all_ext = L"*.*"; |     std::vector<COMDLG_FILTERSPEC>* file_types) { | ||||||
|   // TODO(zcbenz): Should be localized.
 |   DCHECK(file_ext->size() >= ext_desc->size()); | ||||||
|   const std::wstring all_desc = L"All Files"; |  | ||||||
| 
 | 
 | ||||||
|   DCHECK(file_ext.size() >= ext_desc.size()); |   if (file_ext->empty()) | ||||||
| 
 |  | ||||||
|   if (file_ext.empty()) |  | ||||||
|     include_all_files = true; |     include_all_files = true; | ||||||
| 
 | 
 | ||||||
|   std::wstring result; |   for (size_t i = 0; i < file_ext->size(); ++i) { | ||||||
| 
 |     std::wstring ext = (*file_ext)[i]; | ||||||
|   for (size_t i = 0; i < file_ext.size(); ++i) { |  | ||||||
|     std::wstring ext = file_ext[i]; |  | ||||||
|     std::wstring desc; |     std::wstring desc; | ||||||
|     if (i < ext_desc.size()) |     if (i < ext_desc->size()) | ||||||
|       desc = ext_desc[i]; |       desc = (*ext_desc)[i]; | ||||||
| 
 | 
 | ||||||
|     if (ext.empty()) { |     if (ext.empty()) { | ||||||
|       // Force something reasonable to appear in the dialog box if there is no
 |       // Force something reasonable to appear in the dialog box if there is no
 | ||||||
|  | @ -114,230 +105,188 @@ std::wstring FormatFilterForExtensions( | ||||||
|         // the we create a description "QQQ File (.qqq)").
 |         // the we create a description "QQQ File (.qqq)").
 | ||||||
|         include_all_files = true; |         include_all_files = true; | ||||||
|         // TODO(zcbenz): should be localized.
 |         // TODO(zcbenz): should be localized.
 | ||||||
|         desc = base::i18n::ToUpper(WideToUTF16(ext_name)) + L" File (." |         desc = base::i18n::ToUpper(WideToUTF16(ext_name)) + L" File"; | ||||||
|                                                           + ext_name |  | ||||||
|                                                           + L")"; |  | ||||||
|       } |       } | ||||||
|       if (desc.empty()) |       desc += L" (*." + ext_name + L")"; | ||||||
|         desc = L"*." + ext_name; | 
 | ||||||
|  |       // Store the description.
 | ||||||
|  |       ext_desc->push_back(desc); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     result.append(desc.c_str(), desc.size() + 1);  // Append NULL too.
 |     COMDLG_FILTERSPEC spec = { (*ext_desc)[i].c_str(), (*file_ext)[i].c_str() }; | ||||||
|     result.append(ext.c_str(), ext.size() + 1); |     file_types->push_back(spec); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (include_all_files) { |   if (include_all_files) { | ||||||
|     result.append(all_desc.c_str(), all_desc.size() + 1); |     // TODO(zcbenz): Should be localized.
 | ||||||
|     result.append(all_ext.c_str(), all_ext.size() + 1); |     ext_desc->push_back(L"All Files (*.*)"); | ||||||
|   } |     file_ext->push_back(L"*.*"); | ||||||
| 
 | 
 | ||||||
|   result.append(1, '\0');  // Double NULL required.
 |     COMDLG_FILTERSPEC spec = { | ||||||
|   return result; |       (*ext_desc)[ext_desc->size() - 1].c_str(), | ||||||
| } |       (*file_ext)[file_ext->size() - 1].c_str(), | ||||||
| 
 |     }; | ||||||
| // This function takes the output of a SaveAs dialog: a filename, a filter and
 |     file_types->push_back(spec); | ||||||
| // the extension originally suggested to the user (shown in the dialog box) and
 |  | ||||||
| // returns back the filename with the appropriate extension tacked on. If the
 |  | ||||||
| // user requests an unknown extension and is not using the 'All files' filter,
 |  | ||||||
| // the suggested extension will be appended, otherwise we will leave the
 |  | ||||||
| // filename unmodified. |filename| should contain the filename selected in the
 |  | ||||||
| // SaveAs dialog box and may include the path, |filter_selected| should be
 |  | ||||||
| // '*.something', for example '*.*' or it can be blank (which is treated as
 |  | ||||||
| // *.*). |suggested_ext| should contain the extension without the dot (.) in
 |  | ||||||
| // front, for example 'jpg'.
 |  | ||||||
| std::wstring AppendExtensionIfNeeded( |  | ||||||
|     const std::wstring& filename, |  | ||||||
|     const std::wstring& filter_selected, |  | ||||||
|     const std::wstring& suggested_ext) { |  | ||||||
|   DCHECK(!filename.empty()); |  | ||||||
|   std::wstring return_value = filename; |  | ||||||
| 
 |  | ||||||
|   // If we wanted a specific extension, but the user's filename deleted it or
 |  | ||||||
|   // changed it to something that the system doesn't understand, re-append.
 |  | ||||||
|   // Careful: Checking net::GetMimeTypeFromExtension() will only find
 |  | ||||||
|   // extensions with a known MIME type, which many "known" extensions on Windows
 |  | ||||||
|   // don't have.  So we check directly for the "known extension" registry key.
 |  | ||||||
|   std::wstring file_extension( |  | ||||||
|       GetExtensionWithoutLeadingDot(base::FilePath(filename).Extension())); |  | ||||||
|   std::wstring key(L"." + file_extension); |  | ||||||
|   if (!(filter_selected.empty() || filter_selected == L"*.*") && |  | ||||||
|       !base::win::RegKey(HKEY_CLASSES_ROOT, key.c_str(), KEY_READ).Valid() && |  | ||||||
|       file_extension != suggested_ext) { |  | ||||||
|     if (return_value[return_value.length() - 1] != L'.') |  | ||||||
|       return_value.append(L"."); |  | ||||||
|     return_value.append(suggested_ext); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // Strip any trailing dots, which Windows doesn't allow.
 |  | ||||||
|   size_t index = return_value.find_last_not_of(L'.'); |  | ||||||
|   if (index < return_value.size() - 1) |  | ||||||
|     return_value.resize(index + 1); |  | ||||||
| 
 |  | ||||||
|   return return_value; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Enforce visible dialog box.
 |  | ||||||
| UINT_PTR CALLBACK SaveAsDialogHook(HWND dialog, UINT message, |  | ||||||
|                                    WPARAM wparam, LPARAM lparam) { |  | ||||||
|   static const UINT kPrivateMessage = 0x2F3F; |  | ||||||
|   switch (message) { |  | ||||||
|     case WM_INITDIALOG: { |  | ||||||
|       // Do nothing here. Just post a message to defer actual processing.
 |  | ||||||
|       PostMessage(dialog, kPrivateMessage, 0, 0); |  | ||||||
|       return TRUE; |  | ||||||
|     } |  | ||||||
|     case kPrivateMessage: { |  | ||||||
|       // The dialog box is the parent of the current handle.
 |  | ||||||
|       HWND real_dialog = GetParent(dialog); |  | ||||||
| 
 |  | ||||||
|       // Retrieve the final size.
 |  | ||||||
|       RECT dialog_rect; |  | ||||||
|       GetWindowRect(real_dialog, &dialog_rect); |  | ||||||
| 
 |  | ||||||
|       // Verify that the upper left corner is visible.
 |  | ||||||
|       POINT point = { dialog_rect.left, dialog_rect.top }; |  | ||||||
|       HMONITOR monitor1 = MonitorFromPoint(point, MONITOR_DEFAULTTONULL); |  | ||||||
|       point.x = dialog_rect.right; |  | ||||||
|       point.y = dialog_rect.bottom; |  | ||||||
| 
 |  | ||||||
|       // Verify that the lower right corner is visible.
 |  | ||||||
|       HMONITOR monitor2 = MonitorFromPoint(point, MONITOR_DEFAULTTONULL); |  | ||||||
|       if (monitor1 && monitor2) |  | ||||||
|         return 0; |  | ||||||
| 
 |  | ||||||
|       // Some part of the dialog box is not visible, fix it by moving is to the
 |  | ||||||
|       // client rect position of the browser window.
 |  | ||||||
|       HWND parent_window = GetParent(real_dialog); |  | ||||||
|       if (!parent_window) |  | ||||||
|         return 0; |  | ||||||
|       WINDOWINFO parent_info; |  | ||||||
|       parent_info.cbSize = sizeof(WINDOWINFO); |  | ||||||
|       GetWindowInfo(parent_window, &parent_info); |  | ||||||
|       SetWindowPos(real_dialog, NULL, |  | ||||||
|                    parent_info.rcClient.left, |  | ||||||
|                    parent_info.rcClient.top, |  | ||||||
|                    0, 0,  // Size.
 |  | ||||||
|                    SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | |  | ||||||
|                    SWP_NOZORDER); |  | ||||||
| 
 |  | ||||||
|       return 0; |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   return 0; | 
 | ||||||
|  | // Generic class to delegate common open/save dialog's behaviours, users need to
 | ||||||
|  | // call interface methods via GetPtr().
 | ||||||
|  | template <typename T> | ||||||
|  | class FileDialog { | ||||||
|  |  public: | ||||||
|  |   FileDialog(const base::FilePath& default_path, | ||||||
|  |              const std::string title, | ||||||
|  |              int options, | ||||||
|  |              const std::vector<std::wstring>& file_ext, | ||||||
|  |              const std::vector<std::wstring>& desc_ext) | ||||||
|  |       : file_ext_(file_ext), | ||||||
|  |         desc_ext_(desc_ext) { | ||||||
|  |     std::vector<COMDLG_FILTERSPEC> filters; | ||||||
|  |     FormatFilterForExtensions(&file_ext_, &desc_ext_, true, &filters); | ||||||
|  | 
 | ||||||
|  |     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())); | ||||||
|  | 
 | ||||||
|  |     if (!title.empty()) | ||||||
|  |       GetPtr()->SetTitle(UTF8ToUTF16(title).c_str()); | ||||||
|  | 
 | ||||||
|  |     SetDefaultFolder(default_path); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   bool Show(HWND window) { | ||||||
|  |     return dialog_->DoModal(window) == IDOK; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   T* GetDialog() { return dialog_.get(); } | ||||||
|  | 
 | ||||||
|  |   IFileDialog* GetPtr() const { return dialog_->GetPtr(); } | ||||||
|  | 
 | ||||||
|  |   const std::vector<std::wstring> file_ext() const { return file_ext_; } | ||||||
|  | 
 | ||||||
|  |  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<IShellItem> folder_item; | ||||||
|  |     HRESULT hr = SHCreateItemFromParsingName(directory.c_str(), | ||||||
|  |                                              NULL, | ||||||
|  |                                              IID_PPV_ARGS(&folder_item)); | ||||||
|  |     if (SUCCEEDED(hr)) | ||||||
|  |       GetPtr()->SetDefaultFolder(folder_item); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   scoped_ptr<T> dialog_; | ||||||
|  | 
 | ||||||
|  |   std::vector<std::wstring> file_ext_; | ||||||
|  |   std::vector<std::wstring> desc_ext_; | ||||||
|  |   std::vector<COMDLG_FILTERSPEC> filters_; | ||||||
|  | 
 | ||||||
|  |   DISALLOW_COPY_AND_ASSIGN(FileDialog); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| }  // namespace
 | }  // namespace
 | ||||||
| 
 | 
 | ||||||
| bool ShowOpenDialog(const std::string& title, | bool ShowOpenDialog(const std::string& title, | ||||||
|                     const base::FilePath& default_path, |                     const base::FilePath& default_path, | ||||||
|                     int properties, |                     int properties, | ||||||
|                     std::vector<base::FilePath>* paths) { |                     std::vector<base::FilePath>* paths) { | ||||||
|  |   int options = FOS_FORCEFILESYSTEM | FOS_FILEMUSTEXIST; | ||||||
|  |   if (properties & FILE_DIALOG_OPEN_DIRECTORY) | ||||||
|  |     options |= FOS_PICKFOLDERS; | ||||||
|  |   if (properties & FILE_DIALOG_MULTI_SELECTIONS) | ||||||
|  |     options |= FOS_ALLOWMULTISELECT; | ||||||
|  | 
 | ||||||
|  |   FileDialog<CShellFileOpenDialog> open_dialog( | ||||||
|  |       default_path, | ||||||
|  |       title, | ||||||
|  |       options, | ||||||
|  |       std::vector<std::wstring>(), | ||||||
|  |       std::vector<std::wstring>()); | ||||||
|  |   if (!open_dialog.Show(::GetActiveWindow())) | ||||||
|     return false; |     return false; | ||||||
|  | 
 | ||||||
|  |   ATL::CComPtr<IShellItemArray> items; | ||||||
|  |   HRESULT hr = static_cast<IFileOpenDialog*>(open_dialog.GetPtr())->GetResults( | ||||||
|  |       &items); | ||||||
|  |   if (FAILED(hr)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   ATL::CComPtr<IShellItem> item; | ||||||
|  |   DWORD count = 0; | ||||||
|  |   hr = items->GetCount(&count); | ||||||
|  |   if (FAILED(hr)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   paths->reserve(count); | ||||||
|  |   for (DWORD i = 0; i < count; ++i) { | ||||||
|  |     hr = items->GetItemAt(i, &item); | ||||||
|  |     if (FAILED(hr)) | ||||||
|  |       return false; | ||||||
|  | 
 | ||||||
|  |     wchar_t file_name[MAX_PATH]; | ||||||
|  |     hr = CShellFileOpenDialog::GetFileNameFromShellItem( | ||||||
|  |         item, SIGDN_FILESYSPATH, file_name, MAX_PATH); | ||||||
|  |     if (FAILED(hr)) | ||||||
|  |       return false; | ||||||
|  | 
 | ||||||
|  |     paths->push_back(base::FilePath(file_name)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ShowSaveDialog(atom::NativeWindow* window, | bool ShowSaveDialog(atom::NativeWindow* window, | ||||||
|                     const std::string& title, |                     const std::string& title, | ||||||
|                     const base::FilePath& default_path, |                     const base::FilePath& default_path, | ||||||
|                     base::FilePath* path) { |                     base::FilePath* path) { | ||||||
|   std::wstring file_ext = default_path.Extension().insert(0, L"*"); |   // TODO(zcbenz): Accept custom filters from caller.
 | ||||||
|   std::wstring filter = FormatFilterForExtensions( |   std::vector<std::wstring> file_ext; | ||||||
|       std::vector<std::wstring>(1, file_ext), |   std::wstring extension = default_path.Extension(); | ||||||
|       std::vector<std::wstring>(), |   if (!extension.empty()) | ||||||
|       true); |     file_ext.push_back(extension.insert(0, L"*")); | ||||||
| 
 | 
 | ||||||
|   std::wstring file_part = default_path.BaseName().value(); |   FileDialog<CShellFileSaveDialog> save_dialog( | ||||||
|   // If the default_path is a root directory, file_part will be '\', and the
 |       default_path, | ||||||
|   // call to GetSaveFileName below will fail.
 |       title, | ||||||
|   if (file_part.size() == 1 && file_part[0] == L'\\') |       FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT, | ||||||
|     file_part.clear(); |       file_ext, | ||||||
| 
 |       std::vector<std::wstring>()); | ||||||
|   // The size of the in/out buffer in number of characters we pass to win32
 |   if (!save_dialog.Show(window->GetNativeWindow())) | ||||||
|   // GetSaveFileName.  From MSDN "The buffer must be large enough to store the
 |  | ||||||
|   // path and file name string or strings, including the terminating NULL
 |  | ||||||
|   // character.  ... The buffer should be at least 256 characters long.".
 |  | ||||||
|   // _IsValidPathComDlg does a copy expecting at most MAX_PATH, otherwise will
 |  | ||||||
|   // result in an error of FNERR_INVALIDFILENAME.  So we should only pass the
 |  | ||||||
|   // API a buffer of at most MAX_PATH.
 |  | ||||||
|   wchar_t file_name[MAX_PATH]; |  | ||||||
|   base::wcslcpy(file_name, file_part.c_str(), arraysize(file_name)); |  | ||||||
| 
 |  | ||||||
|   OPENFILENAME save_as; |  | ||||||
|   // We must do this otherwise the ofn's FlagsEx may be initialized to random
 |  | ||||||
|   // junk in release builds which can cause the Places Bar not to show up!
 |  | ||||||
|   ZeroMemory(&save_as, sizeof(save_as)); |  | ||||||
|   save_as.lStructSize = sizeof(OPENFILENAME); |  | ||||||
|   save_as.hwndOwner = window->GetNativeWindow(); |  | ||||||
|   save_as.hInstance = NULL; |  | ||||||
| 
 |  | ||||||
|   save_as.lpstrFilter = filter.empty() ? NULL : filter.c_str(); |  | ||||||
| 
 |  | ||||||
|   save_as.lpstrCustomFilter = NULL; |  | ||||||
|   save_as.nMaxCustFilter = 0; |  | ||||||
|   save_as.nFilterIndex = 1; |  | ||||||
|   save_as.lpstrFile = file_name; |  | ||||||
|   save_as.nMaxFile = arraysize(file_name); |  | ||||||
|   save_as.lpstrFileTitle = NULL; |  | ||||||
|   save_as.nMaxFileTitle = 0; |  | ||||||
| 
 |  | ||||||
|   // Set up the initial directory for the dialog.
 |  | ||||||
|   std::wstring directory; |  | ||||||
|   if (IsDirectory(default_path)) { |  | ||||||
|     directory = default_path.value(); |  | ||||||
|     file_part.clear(); |  | ||||||
|   } else { |  | ||||||
|     directory = default_path.DirName().value(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   save_as.lpstrInitialDir = directory.c_str(); |  | ||||||
|   save_as.lpstrTitle = NULL; |  | ||||||
|   save_as.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLESIZING | |  | ||||||
|                   OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; |  | ||||||
|   save_as.lpstrDefExt = NULL;  // default extension, ignored for now.
 |  | ||||||
|   save_as.lCustData = NULL; |  | ||||||
| 
 |  | ||||||
|   if (base::win::GetVersion() < base::win::VERSION_VISTA) { |  | ||||||
|     // The save as on Windows XP remembers its last position,
 |  | ||||||
|     // and if the screen resolution changed, it will be off screen.
 |  | ||||||
|     save_as.Flags |= OFN_ENABLEHOOK; |  | ||||||
|     save_as.lpfnHook = &SaveAsDialogHook; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // Must be NULL or 0.
 |  | ||||||
|   save_as.pvReserved = NULL; |  | ||||||
|   save_as.dwReserved = 0; |  | ||||||
| 
 |  | ||||||
|   if (!GetSaveFileName(&save_as)) { |  | ||||||
|     // Zero means the dialog was closed, otherwise we had an error.
 |  | ||||||
|     DWORD error_code = CommDlgExtendedError(); |  | ||||||
|     if (error_code != 0) { |  | ||||||
|       NOTREACHED() << "GetSaveFileName failed with code: " << error_code; |  | ||||||
|     } |  | ||||||
|     return false; |     return false; | ||||||
|  | 
 | ||||||
|  |   wchar_t file_name[MAX_PATH]; | ||||||
|  |   HRESULT hr = save_dialog.GetDialog()->GetFilePath(file_name, MAX_PATH); | ||||||
|  |   if (FAILED(hr)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   // 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; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // Return the user's choice.
 |   *path = base::FilePath(file_name); | ||||||
|   *path = base::FilePath(); |  | ||||||
| 
 |  | ||||||
|   // Figure out what filter got selected from the vector with embedded nulls.
 |  | ||||||
|   // NOTE: The filter contains a string with embedded nulls, such as:
 |  | ||||||
|   // JPG Image\0*.jpg\0All files\0*.*\0\0
 |  | ||||||
|   // The filter index is 1-based index for which pair got selected. So, using
 |  | ||||||
|   // the example above, if the first index was selected we need to skip 1
 |  | ||||||
|   // instance of null to get to "*.jpg".
 |  | ||||||
|   std::vector<std::wstring> filters; |  | ||||||
|   if (!filter.empty() && save_as.nFilterIndex > 0) |  | ||||||
|     base::SplitString(filter, '\0', &filters); |  | ||||||
|   std::wstring filter_selected; |  | ||||||
|   if (!filters.empty()) |  | ||||||
|     filter_selected = filters[(2 * (save_as.nFilterIndex - 1)) + 1]; |  | ||||||
| 
 |  | ||||||
|   // Get the extension that was suggested to the user (when the Save As dialog
 |  | ||||||
|   // was opened).
 |  | ||||||
|   std::wstring suggested_ext = |  | ||||||
|     GetExtensionWithoutLeadingDot(default_path.Extension()); |  | ||||||
| 
 |  | ||||||
|   *path = base::FilePath(AppendExtensionIfNeeded( |  | ||||||
|         save_as.lpstrFile, filter_selected, suggested_ext)); |  | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Cheng Zhao
				Cheng Zhao