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 |