| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/shell_dialogs/select_file_dialog_win.h" | 5 #include "ui/shell_dialogs/select_file_dialog_win.h" |
| 6 | 6 |
| 7 #include <shlobj.h> | 7 #include <shlobj.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <set> | 10 #include <set> |
| 11 | 11 |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/file_util.h" | 13 #include "base/file_util.h" |
| 14 #include "base/files/file_path.h" | 14 #include "base/files/file_path.h" |
| 15 #include "base/i18n/case_conversion.h" | 15 #include "base/i18n/case_conversion.h" |
| 16 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
| 17 #include "base/message_loop/message_loop_proxy.h" | 17 #include "base/message_loop/message_loop_proxy.h" |
| 18 #include "base/strings/string_split.h" | |
| 19 #include "base/strings/utf_string_conversions.h" | 18 #include "base/strings/utf_string_conversions.h" |
| 20 #include "base/threading/thread.h" | 19 #include "base/threading/thread.h" |
| 21 #include "base/win/metro.h" | 20 #include "base/tuple.h" |
| 22 #include "base/win/registry.h" | 21 #include "base/win/registry.h" |
| 23 #include "base/win/scoped_comptr.h" | 22 #include "base/win/scoped_comptr.h" |
| 24 #include "base/win/shortcut.h" | 23 #include "base/win/shortcut.h" |
| 25 #include "base/win/windows_version.h" | |
| 26 #include "grit/ui_strings.h" | 24 #include "grit/ui_strings.h" |
| 27 #include "ui/aura/window.h" | 25 #include "ui/aura/window.h" |
| 28 #include "ui/aura/window_event_dispatcher.h" | 26 #include "ui/aura/window_event_dispatcher.h" |
| 29 #include "ui/aura/window_tree_host.h" | 27 #include "ui/aura/window_tree_host.h" |
| 30 #include "ui/base/l10n/l10n_util.h" | 28 #include "ui/base/l10n/l10n_util.h" |
| 31 #include "ui/base/win/open_file_name_win.h" | 29 #include "ui/base/win/open_file_name_win.h" |
| 32 #include "ui/gfx/native_widget_types.h" | 30 #include "ui/gfx/native_widget_types.h" |
| 33 #include "ui/shell_dialogs/base_shell_dialog_win.h" | 31 #include "ui/shell_dialogs/base_shell_dialog_win.h" |
| 34 #include "ui/shell_dialogs/shell_dialogs_delegate.h" | 32 #include "ui/shell_dialogs/shell_dialogs_delegate.h" |
| 35 #include "win8/viewer/metro_viewer_process_host.h" | 33 #include "win8/viewer/metro_viewer_process_host.h" |
| 36 | 34 |
| 37 namespace { | 35 namespace { |
| 38 | 36 |
| 39 bool CallBuiltinGetOpenFileName(OPENFILENAME* ofn) { | 37 bool CallBuiltinGetOpenFileName(OPENFILENAME* ofn) { |
| 40 return ::GetOpenFileName(ofn) == TRUE; | 38 return ::GetOpenFileName(ofn) == TRUE; |
| 41 } | 39 } |
| 42 | 40 |
| 41 bool CallBuiltinGetSaveFileName(OPENFILENAME* ofn) { |
| 42 return ::GetSaveFileName(ofn) == TRUE; |
| 43 } |
| 44 |
| 43 // Given |extension|, if it's not empty, then remove the leading dot. | 45 // Given |extension|, if it's not empty, then remove the leading dot. |
| 44 std::wstring GetExtensionWithoutLeadingDot(const std::wstring& extension) { | 46 std::wstring GetExtensionWithoutLeadingDot(const std::wstring& extension) { |
| 45 DCHECK(extension.empty() || extension[0] == L'.'); | 47 DCHECK(extension.empty() || extension[0] == L'.'); |
| 46 return extension.empty() ? extension : extension.substr(1); | 48 return extension.empty() ? extension : extension.substr(1); |
| 47 } | 49 } |
| 48 | 50 |
| 49 // Diverts to a metro-specific implementation as appropriate. | |
| 50 bool CallGetSaveFileName(OPENFILENAME* ofn) { | |
| 51 HMODULE metro_module = base::win::GetMetroModule(); | |
| 52 if (metro_module != NULL) { | |
| 53 typedef BOOL (*MetroGetSaveFileName)(OPENFILENAME*); | |
| 54 MetroGetSaveFileName metro_get_save_file_name = | |
| 55 reinterpret_cast<MetroGetSaveFileName>( | |
| 56 ::GetProcAddress(metro_module, "MetroGetSaveFileName")); | |
| 57 if (metro_get_save_file_name == NULL) { | |
| 58 NOTREACHED(); | |
| 59 return false; | |
| 60 } | |
| 61 | |
| 62 return metro_get_save_file_name(ofn) == TRUE; | |
| 63 } else { | |
| 64 return GetSaveFileName(ofn) == TRUE; | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 // Distinguish directories from regular files. | 51 // Distinguish directories from regular files. |
| 69 bool IsDirectory(const base::FilePath& path) { | 52 bool IsDirectory(const base::FilePath& path) { |
| 70 base::File::Info file_info; | 53 base::File::Info file_info; |
| 71 return base::GetFileInfo(path, &file_info) ? | 54 return base::GetFileInfo(path, &file_info) ? |
| 72 file_info.is_directory : path.EndsWithSeparator(); | 55 file_info.is_directory : path.EndsWithSeparator(); |
| 73 } | 56 } |
| 74 | 57 |
| 75 // Get the file type description from the registry. This will be "Text Document" | 58 // Get the file type description from the registry. This will be "Text Document" |
| 76 // for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't | 59 // for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't |
| 77 // have an entry for the file type, we return false, true if the description was | 60 // have an entry for the file type, we return false, true if the description was |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 | 149 |
| 167 if (include_all_files) { | 150 if (include_all_files) { |
| 168 result.append(all_desc.c_str(), all_desc.size() + 1); | 151 result.append(all_desc.c_str(), all_desc.size() + 1); |
| 169 result.append(all_ext.c_str(), all_ext.size() + 1); | 152 result.append(all_ext.c_str(), all_ext.size() + 1); |
| 170 } | 153 } |
| 171 | 154 |
| 172 result.append(1, '\0'); // Double NULL required. | 155 result.append(1, '\0'); // Double NULL required. |
| 173 return result; | 156 return result; |
| 174 } | 157 } |
| 175 | 158 |
| 176 // Enforce visible dialog box. | |
| 177 UINT_PTR CALLBACK SaveAsDialogHook(HWND dialog, UINT message, | |
| 178 WPARAM wparam, LPARAM lparam) { | |
| 179 static const UINT kPrivateMessage = 0x2F3F; | |
| 180 switch (message) { | |
| 181 case WM_INITDIALOG: { | |
| 182 // Do nothing here. Just post a message to defer actual processing. | |
| 183 PostMessage(dialog, kPrivateMessage, 0, 0); | |
| 184 return TRUE; | |
| 185 } | |
| 186 case kPrivateMessage: { | |
| 187 // The dialog box is the parent of the current handle. | |
| 188 HWND real_dialog = GetParent(dialog); | |
| 189 | |
| 190 // Retrieve the final size. | |
| 191 RECT dialog_rect; | |
| 192 GetWindowRect(real_dialog, &dialog_rect); | |
| 193 | |
| 194 // Verify that the upper left corner is visible. | |
| 195 POINT point = { dialog_rect.left, dialog_rect.top }; | |
| 196 HMONITOR monitor1 = MonitorFromPoint(point, MONITOR_DEFAULTTONULL); | |
| 197 point.x = dialog_rect.right; | |
| 198 point.y = dialog_rect.bottom; | |
| 199 | |
| 200 // Verify that the lower right corner is visible. | |
| 201 HMONITOR monitor2 = MonitorFromPoint(point, MONITOR_DEFAULTTONULL); | |
| 202 if (monitor1 && monitor2) | |
| 203 return 0; | |
| 204 | |
| 205 // Some part of the dialog box is not visible, fix it by moving is to the | |
| 206 // client rect position of the browser window. | |
| 207 HWND parent_window = GetParent(real_dialog); | |
| 208 if (!parent_window) | |
| 209 return 0; | |
| 210 WINDOWINFO parent_info; | |
| 211 parent_info.cbSize = sizeof(WINDOWINFO); | |
| 212 GetWindowInfo(parent_window, &parent_info); | |
| 213 SetWindowPos(real_dialog, NULL, | |
| 214 parent_info.rcClient.left, | |
| 215 parent_info.rcClient.top, | |
| 216 0, 0, // Size. | |
| 217 SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | | |
| 218 SWP_NOZORDER); | |
| 219 | |
| 220 return 0; | |
| 221 } | |
| 222 } | |
| 223 return 0; | |
| 224 } | |
| 225 | |
| 226 // Prompt the user for location to save a file. | |
| 227 // Callers should provide the filter string, and also a filter index. | |
| 228 // The parameter |index| indicates the initial index of filter description | |
| 229 // and filter pattern for the dialog box. If |index| is zero or greater than | |
| 230 // the number of total filter types, the system uses the first filter in the | |
| 231 // |filter| buffer. |index| is used to specify the initial selected extension, | |
| 232 // and when done contains the extension the user chose. The parameter | |
| 233 // |final_name| returns the file name which contains the drive designator, | |
| 234 // path, file name, and extension of the user selected file name. |def_ext| is | |
| 235 // the default extension to give to the file if the user did not enter an | |
| 236 // extension. If |ignore_suggested_ext| is true, any file extension contained in | |
| 237 // |suggested_name| will not be used to generate the file name. This is useful | |
| 238 // in the case of saving web pages, where we know the extension type already and | |
| 239 // where |suggested_name| may contain a '.' character as a valid part of the | |
| 240 // name, thus confusing our extension detection code. | |
| 241 bool SaveFileAsWithFilter(HWND owner, | |
| 242 const std::wstring& suggested_name, | |
| 243 const std::wstring& filter, | |
| 244 const std::wstring& def_ext, | |
| 245 bool ignore_suggested_ext, | |
| 246 unsigned* index, | |
| 247 std::wstring* final_name) { | |
| 248 DCHECK(final_name); | |
| 249 // Having an empty filter makes for a bad user experience. We should always | |
| 250 // specify a filter when saving. | |
| 251 DCHECK(!filter.empty()); | |
| 252 const base::FilePath suggested_path(suggested_name); | |
| 253 std::wstring file_part = suggested_path.BaseName().value(); | |
| 254 // If the suggested_name is a root directory, file_part will be '\', and the | |
| 255 // call to GetSaveFileName below will fail. | |
| 256 if (file_part.size() == 1 && file_part[0] == L'\\') | |
| 257 file_part.clear(); | |
| 258 | |
| 259 // The size of the in/out buffer in number of characters we pass to win32 | |
| 260 // GetSaveFileName. From MSDN "The buffer must be large enough to store the | |
| 261 // path and file name string or strings, including the terminating NULL | |
| 262 // character. ... The buffer should be at least 256 characters long.". | |
| 263 // _IsValidPathComDlg does a copy expecting at most MAX_PATH, otherwise will | |
| 264 // result in an error of FNERR_INVALIDFILENAME. So we should only pass the | |
| 265 // API a buffer of at most MAX_PATH. | |
| 266 wchar_t file_name[MAX_PATH]; | |
| 267 base::wcslcpy(file_name, file_part.c_str(), arraysize(file_name)); | |
| 268 | |
| 269 OPENFILENAME save_as; | |
| 270 // We must do this otherwise the ofn's FlagsEx may be initialized to random | |
| 271 // junk in release builds which can cause the Places Bar not to show up! | |
| 272 ZeroMemory(&save_as, sizeof(save_as)); | |
| 273 save_as.lStructSize = sizeof(OPENFILENAME); | |
| 274 save_as.hwndOwner = owner; | |
| 275 save_as.hInstance = NULL; | |
| 276 | |
| 277 save_as.lpstrFilter = filter.empty() ? NULL : filter.c_str(); | |
| 278 | |
| 279 save_as.lpstrCustomFilter = NULL; | |
| 280 save_as.nMaxCustFilter = 0; | |
| 281 save_as.nFilterIndex = *index; | |
| 282 save_as.lpstrFile = file_name; | |
| 283 save_as.nMaxFile = arraysize(file_name); | |
| 284 save_as.lpstrFileTitle = NULL; | |
| 285 save_as.nMaxFileTitle = 0; | |
| 286 | |
| 287 // Set up the initial directory for the dialog. | |
| 288 std::wstring directory; | |
| 289 if (!suggested_name.empty()) { | |
| 290 if (IsDirectory(suggested_path)) { | |
| 291 directory = suggested_path.value(); | |
| 292 file_part.clear(); | |
| 293 } else { | |
| 294 directory = suggested_path.DirName().value(); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 save_as.lpstrInitialDir = directory.c_str(); | |
| 299 save_as.lpstrTitle = NULL; | |
| 300 save_as.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLESIZING | | |
| 301 OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; | |
| 302 save_as.lpstrDefExt = &def_ext[0]; | |
| 303 save_as.lCustData = NULL; | |
| 304 | |
| 305 if (base::win::GetVersion() < base::win::VERSION_VISTA) { | |
| 306 // The save as on Windows XP remembers its last position, | |
| 307 // and if the screen resolution changed, it will be off screen. | |
| 308 save_as.Flags |= OFN_ENABLEHOOK; | |
| 309 save_as.lpfnHook = &SaveAsDialogHook; | |
| 310 } | |
| 311 | |
| 312 // Must be NULL or 0. | |
| 313 save_as.pvReserved = NULL; | |
| 314 save_as.dwReserved = 0; | |
| 315 | |
| 316 if (!CallGetSaveFileName(&save_as)) { | |
| 317 // Zero means the dialog was closed, otherwise we had an error. | |
| 318 DWORD error_code = CommDlgExtendedError(); | |
| 319 if (error_code != 0) { | |
| 320 NOTREACHED() << "GetSaveFileName failed with code: " << error_code; | |
| 321 } | |
| 322 return false; | |
| 323 } | |
| 324 | |
| 325 // Return the user's choice. | |
| 326 final_name->assign(save_as.lpstrFile); | |
| 327 *index = save_as.nFilterIndex; | |
| 328 | |
| 329 // Figure out what filter got selected from the vector with embedded nulls. | |
| 330 // NOTE: The filter contains a string with embedded nulls, such as: | |
| 331 // JPG Image\0*.jpg\0All files\0*.*\0\0 | |
| 332 // The filter index is 1-based index for which pair got selected. So, using | |
| 333 // the example above, if the first index was selected we need to skip 1 | |
| 334 // instance of null to get to "*.jpg". | |
| 335 std::vector<std::wstring> filters; | |
| 336 if (!filter.empty() && save_as.nFilterIndex > 0) | |
| 337 base::SplitString(filter, '\0', &filters); | |
| 338 std::wstring filter_selected; | |
| 339 if (!filters.empty()) | |
| 340 filter_selected = filters[(2 * (save_as.nFilterIndex - 1)) + 1]; | |
| 341 | |
| 342 // Get the extension that was suggested to the user (when the Save As dialog | |
| 343 // was opened). For saving web pages, we skip this step since there may be | |
| 344 // 'extension characters' in the title of the web page. | |
| 345 std::wstring suggested_ext; | |
| 346 if (!ignore_suggested_ext) | |
| 347 suggested_ext = GetExtensionWithoutLeadingDot(suggested_path.Extension()); | |
| 348 | |
| 349 // If we can't get the extension from the suggested_name, we use the default | |
| 350 // extension passed in. This is to cover cases like when saving a web page, | |
| 351 // where we get passed in a name without an extension and a default extension | |
| 352 // along with it. | |
| 353 if (suggested_ext.empty()) | |
| 354 suggested_ext = def_ext; | |
| 355 | |
| 356 *final_name = | |
| 357 ui::AppendExtensionIfNeeded(*final_name, filter_selected, suggested_ext); | |
| 358 return true; | |
| 359 } | |
| 360 | |
| 361 // Implementation of SelectFileDialog that shows a Windows common dialog for | 159 // Implementation of SelectFileDialog that shows a Windows common dialog for |
| 362 // choosing a file or folder. | 160 // choosing a file or folder. |
| 363 class SelectFileDialogImpl : public ui::SelectFileDialog, | 161 class SelectFileDialogImpl : public ui::SelectFileDialog, |
| 364 public ui::BaseShellDialogImpl { | 162 public ui::BaseShellDialogImpl { |
| 365 public: | 163 public: |
| 366 SelectFileDialogImpl( | 164 SelectFileDialogImpl( |
| 367 Listener* listener, | 165 Listener* listener, |
| 368 ui::SelectFilePolicy* policy, | 166 ui::SelectFilePolicy* policy, |
| 369 const base::Callback<bool(OPENFILENAME*)>& get_open_file_name_impl); | 167 const base::Callback<bool(OPENFILENAME*)>& get_open_file_name_impl, |
| 168 const base::Callback<bool(OPENFILENAME*)>& get_save_file_name_impl); |
| 370 | 169 |
| 371 // BaseShellDialog implementation: | 170 // BaseShellDialog implementation: |
| 372 virtual bool IsRunning(gfx::NativeWindow owning_window) const OVERRIDE; | 171 virtual bool IsRunning(gfx::NativeWindow owning_window) const OVERRIDE; |
| 373 virtual void ListenerDestroyed() OVERRIDE; | 172 virtual void ListenerDestroyed() OVERRIDE; |
| 374 | 173 |
| 375 protected: | 174 protected: |
| 376 // SelectFileDialog implementation: | 175 // SelectFileDialog implementation: |
| 377 virtual void SelectFileImpl( | 176 virtual void SelectFileImpl( |
| 378 Type type, | 177 Type type, |
| 379 const base::string16& title, | 178 const base::string16& title, |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 419 RunState run_state; | 218 RunState run_state; |
| 420 scoped_refptr<base::MessageLoopProxy> ui_proxy; | 219 scoped_refptr<base::MessageLoopProxy> ui_proxy; |
| 421 HWND owner; | 220 HWND owner; |
| 422 void* params; | 221 void* params; |
| 423 }; | 222 }; |
| 424 | 223 |
| 425 // Shows the file selection dialog modal to |owner| and calls the result | 224 // Shows the file selection dialog modal to |owner| and calls the result |
| 426 // back on the ui thread. Run on the dialog thread. | 225 // back on the ui thread. Run on the dialog thread. |
| 427 void ExecuteSelectFile(const ExecuteSelectParams& params); | 226 void ExecuteSelectFile(const ExecuteSelectParams& params); |
| 428 | 227 |
| 228 // Prompt the user for location to save a file. |
| 229 // Callers should provide the filter string, and also a filter index. |
| 230 // The parameter |index| indicates the initial index of filter description |
| 231 // and filter pattern for the dialog box. If |index| is zero or greater than |
| 232 // the number of total filter types, the system uses the first filter in the |
| 233 // |filter| buffer. |index| is used to specify the initial selected extension, |
| 234 // and when done contains the extension the user chose. The parameter |
| 235 // |final_name| returns the file name which contains the drive designator, |
| 236 // path, file name, and extension of the user selected file name. |def_ext| is |
| 237 // the default extension to give to the file if the user did not enter an |
| 238 // extension. If |ignore_suggested_ext| is true, any file extension contained |
| 239 // in |suggested_name| will not be used to generate the file name. This is |
| 240 // useful in the case of saving web pages, where we know the extension type |
| 241 // already and where |suggested_name| may contain a '.' character as a valid |
| 242 // part of the name, thus confusing our extension detection code. |
| 243 bool SaveFileAsWithFilter(HWND owner, |
| 244 const std::wstring& suggested_name, |
| 245 const std::wstring& filter, |
| 246 const std::wstring& def_ext, |
| 247 bool ignore_suggested_ext, |
| 248 unsigned* index, |
| 249 std::wstring* final_name); |
| 250 |
| 429 // Notifies the listener that a folder was chosen. Run on the ui thread. | 251 // Notifies the listener that a folder was chosen. Run on the ui thread. |
| 430 void FileSelected(const base::FilePath& path, int index, | 252 void FileSelected(const base::FilePath& path, int index, |
| 431 void* params, RunState run_state); | 253 void* params, RunState run_state); |
| 432 | 254 |
| 433 // Notifies listener that multiple files were chosen. Run on the ui thread. | 255 // Notifies listener that multiple files were chosen. Run on the ui thread. |
| 434 void MultiFilesSelected(const std::vector<base::FilePath>& paths, | 256 void MultiFilesSelected(const std::vector<base::FilePath>& paths, |
| 435 void* params, | 257 void* params, |
| 436 RunState run_state); | 258 RunState run_state); |
| 437 | 259 |
| 438 // Notifies the listener that no file was chosen (the action was canceled). | 260 // Notifies the listener that no file was chosen (the action was canceled). |
| (...skipping 30 matching lines...) Expand all Loading... |
| 469 | 291 |
| 470 virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE; | 292 virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE; |
| 471 | 293 |
| 472 // Returns the filter to be used while displaying the open/save file dialog. | 294 // Returns the filter to be used while displaying the open/save file dialog. |
| 473 // This is computed from the extensions for the file types being opened. | 295 // This is computed from the extensions for the file types being opened. |
| 474 // |file_types| can be NULL in which case the returned filter will be empty. | 296 // |file_types| can be NULL in which case the returned filter will be empty. |
| 475 base::string16 GetFilterForFileTypes(const FileTypeInfo* file_types); | 297 base::string16 GetFilterForFileTypes(const FileTypeInfo* file_types); |
| 476 | 298 |
| 477 bool has_multiple_file_type_choices_; | 299 bool has_multiple_file_type_choices_; |
| 478 base::Callback<bool(OPENFILENAME*)> get_open_file_name_impl_; | 300 base::Callback<bool(OPENFILENAME*)> get_open_file_name_impl_; |
| 301 base::Callback<bool(OPENFILENAME*)> get_save_file_name_impl_; |
| 479 | 302 |
| 480 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl); | 303 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl); |
| 481 }; | 304 }; |
| 482 | 305 |
| 483 SelectFileDialogImpl::SelectFileDialogImpl( | 306 SelectFileDialogImpl::SelectFileDialogImpl( |
| 484 Listener* listener, | 307 Listener* listener, |
| 485 ui::SelectFilePolicy* policy, | 308 ui::SelectFilePolicy* policy, |
| 486 const base::Callback<bool(OPENFILENAME*)>& get_open_file_name_impl) | 309 const base::Callback<bool(OPENFILENAME*)>& get_open_file_name_impl, |
| 310 const base::Callback<bool(OPENFILENAME*)>& get_save_file_name_impl) |
| 487 : SelectFileDialog(listener, policy), | 311 : SelectFileDialog(listener, policy), |
| 488 BaseShellDialogImpl(), | 312 BaseShellDialogImpl(), |
| 489 has_multiple_file_type_choices_(false), | 313 has_multiple_file_type_choices_(false), |
| 490 get_open_file_name_impl_(get_open_file_name_impl) { | 314 get_open_file_name_impl_(get_open_file_name_impl), |
| 315 get_save_file_name_impl_(get_save_file_name_impl) { |
| 491 } | 316 } |
| 492 | 317 |
| 493 SelectFileDialogImpl::~SelectFileDialogImpl() { | 318 SelectFileDialogImpl::~SelectFileDialogImpl() { |
| 494 } | 319 } |
| 495 | 320 |
| 496 void SelectFileDialogImpl::SelectFileImpl( | 321 void SelectFileDialogImpl::SelectFileImpl( |
| 497 Type type, | 322 Type type, |
| 498 const base::string16& title, | 323 const base::string16& title, |
| 499 const base::FilePath& default_path, | 324 const base::FilePath& default_path, |
| 500 const FileTypeInfo* file_types, | 325 const FileTypeInfo* file_types, |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 633 base::Bind(&SelectFileDialogImpl::FileSelected, this, path, | 458 base::Bind(&SelectFileDialogImpl::FileSelected, this, path, |
| 634 filter_index, params.params, params.run_state)); | 459 filter_index, params.params, params.run_state)); |
| 635 } else { | 460 } else { |
| 636 params.ui_proxy->PostTask( | 461 params.ui_proxy->PostTask( |
| 637 FROM_HERE, | 462 FROM_HERE, |
| 638 base::Bind(&SelectFileDialogImpl::FileNotSelected, this, params.params, | 463 base::Bind(&SelectFileDialogImpl::FileNotSelected, this, params.params, |
| 639 params.run_state)); | 464 params.run_state)); |
| 640 } | 465 } |
| 641 } | 466 } |
| 642 | 467 |
| 468 bool SelectFileDialogImpl::SaveFileAsWithFilter( |
| 469 HWND owner, |
| 470 const std::wstring& suggested_name, |
| 471 const std::wstring& filter, |
| 472 const std::wstring& def_ext, |
| 473 bool ignore_suggested_ext, |
| 474 unsigned* index, |
| 475 std::wstring* final_name) { |
| 476 DCHECK(final_name); |
| 477 // Having an empty filter makes for a bad user experience. We should always |
| 478 // specify a filter when saving. |
| 479 DCHECK(!filter.empty()); |
| 480 |
| 481 ui::win::OpenFileName save_as(owner, |
| 482 OFN_OVERWRITEPROMPT | OFN_EXPLORER | |
| 483 OFN_ENABLESIZING | OFN_NOCHANGEDIR | |
| 484 OFN_PATHMUSTEXIST); |
| 485 |
| 486 const base::FilePath suggested_path = base::FilePath(suggested_name); |
| 487 if (!suggested_name.empty()) { |
| 488 base::FilePath suggested_file_name; |
| 489 base::FilePath suggested_directory; |
| 490 if (IsDirectory(suggested_path)) { |
| 491 suggested_directory = suggested_path; |
| 492 } else { |
| 493 suggested_directory = suggested_path.DirName(); |
| 494 suggested_file_name = suggested_path.BaseName(); |
| 495 // If the suggested_name is a root directory, file_part will be '\', and |
| 496 // the call to GetSaveFileName below will fail. |
| 497 if (suggested_file_name.value() == L"\\") |
| 498 suggested_file_name.clear(); |
| 499 } |
| 500 save_as.SetInitialSelection(suggested_directory, suggested_file_name); |
| 501 } |
| 502 |
| 503 save_as.GetOPENFILENAME()->lpstrFilter = |
| 504 filter.empty() ? NULL : filter.c_str(); |
| 505 save_as.GetOPENFILENAME()->nFilterIndex = *index; |
| 506 save_as.GetOPENFILENAME()->lpstrDefExt = &def_ext[0]; |
| 507 save_as.MaybeInstallWindowPositionHookForSaveAsOnXP(); |
| 508 |
| 509 if (!get_save_file_name_impl_.Run(save_as.GetOPENFILENAME())) |
| 510 return false; |
| 511 |
| 512 // Return the user's choice. |
| 513 final_name->assign(save_as.GetOPENFILENAME()->lpstrFile); |
| 514 *index = save_as.GetOPENFILENAME()->nFilterIndex; |
| 515 |
| 516 // Figure out what filter got selected. The filter index is 1-based. |
| 517 std::wstring filter_selected; |
| 518 if (*index > 0) { |
| 519 std::vector<Tuple2<base::string16, base::string16> > filters = |
| 520 ui::win::OpenFileName::GetFilters(save_as.GetOPENFILENAME()); |
| 521 if (*index > filters.size()) |
| 522 NOTREACHED() << "Invalid filter index."; |
| 523 else |
| 524 filter_selected = filters[*index - 1].b; |
| 525 } |
| 526 |
| 527 // Get the extension that was suggested to the user (when the Save As dialog |
| 528 // was opened). For saving web pages, we skip this step since there may be |
| 529 // 'extension characters' in the title of the web page. |
| 530 std::wstring suggested_ext; |
| 531 if (!ignore_suggested_ext) |
| 532 suggested_ext = GetExtensionWithoutLeadingDot(suggested_path.Extension()); |
| 533 |
| 534 // If we can't get the extension from the suggested_name, we use the default |
| 535 // extension passed in. This is to cover cases like when saving a web page, |
| 536 // where we get passed in a name without an extension and a default extension |
| 537 // along with it. |
| 538 if (suggested_ext.empty()) |
| 539 suggested_ext = def_ext; |
| 540 |
| 541 *final_name = |
| 542 ui::AppendExtensionIfNeeded(*final_name, filter_selected, suggested_ext); |
| 543 return true; |
| 544 } |
| 545 |
| 643 void SelectFileDialogImpl::FileSelected(const base::FilePath& selected_folder, | 546 void SelectFileDialogImpl::FileSelected(const base::FilePath& selected_folder, |
| 644 int index, | 547 int index, |
| 645 void* params, | 548 void* params, |
| 646 RunState run_state) { | 549 RunState run_state) { |
| 647 if (listener_) | 550 if (listener_) |
| 648 listener_->FileSelected(selected_folder, index, params); | 551 listener_->FileSelected(selected_folder, index, params); |
| 649 EndRun(run_state); | 552 EndRun(run_state); |
| 650 } | 553 } |
| 651 | 554 |
| 652 void SelectFileDialogImpl::MultiFilesSelected( | 555 void SelectFileDialogImpl::MultiFilesSelected( |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 849 size_t index = return_value.find_last_not_of(L'.'); | 752 size_t index = return_value.find_last_not_of(L'.'); |
| 850 if (index < return_value.size() - 1) | 753 if (index < return_value.size() - 1) |
| 851 return_value.resize(index + 1); | 754 return_value.resize(index + 1); |
| 852 | 755 |
| 853 return return_value; | 756 return return_value; |
| 854 } | 757 } |
| 855 | 758 |
| 856 SelectFileDialog* CreateWinSelectFileDialog( | 759 SelectFileDialog* CreateWinSelectFileDialog( |
| 857 SelectFileDialog::Listener* listener, | 760 SelectFileDialog::Listener* listener, |
| 858 SelectFilePolicy* policy, | 761 SelectFilePolicy* policy, |
| 859 const base::Callback<bool(OPENFILENAME* ofn)>& get_open_file_name_impl) { | 762 const base::Callback<bool(OPENFILENAME* ofn)>& get_open_file_name_impl, |
| 860 return new SelectFileDialogImpl(listener, policy, get_open_file_name_impl); | 763 const base::Callback<bool(OPENFILENAME* ofn)>& get_save_file_name_impl) { |
| 764 return new SelectFileDialogImpl( |
| 765 listener, policy, get_open_file_name_impl, get_save_file_name_impl); |
| 861 } | 766 } |
| 862 | 767 |
| 863 SelectFileDialog* CreateDefaultWinSelectFileDialog( | 768 SelectFileDialog* CreateDefaultWinSelectFileDialog( |
| 864 SelectFileDialog::Listener* listener, | 769 SelectFileDialog::Listener* listener, |
| 865 SelectFilePolicy* policy) { | 770 SelectFilePolicy* policy) { |
| 866 return CreateWinSelectFileDialog( | 771 return CreateWinSelectFileDialog(listener, |
| 867 listener, policy, base::Bind(&CallBuiltinGetOpenFileName)); | 772 policy, |
| 773 base::Bind(&CallBuiltinGetOpenFileName), |
| 774 base::Bind(&CallBuiltinGetSaveFileName)); |
| 868 } | 775 } |
| 869 | 776 |
| 870 } // namespace ui | 777 } // namespace ui |
| OLD | NEW |