1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/base/win/open_file_name_win.h" 6 7 #include "base/files/file_path.h" 8 #include "base/strings/string_util.h" 9 #include "base/win/windows_version.h" 10 11 namespace ui { 12 namespace win { 13 14 namespace { 15 16 // Ensures that the Save As dialog is on-screen. 17 UINT_PTR CALLBACK SaveAsDialogHook(HWND dialog, UINT message, 18 WPARAM wparam, LPARAM lparam) { 19 static const UINT kPrivateMessage = 0x2F3F; 20 switch (message) { 21 case WM_INITDIALOG: { 22 // Do nothing here. Just post a message to defer actual processing. 23 ::PostMessage(dialog, kPrivateMessage, 0, 0); 24 return TRUE; 25 } 26 case kPrivateMessage: { 27 // The dialog box is the parent of the current handle. 28 HWND real_dialog = ::GetParent(dialog); 29 30 // Retrieve the final size. 31 RECT dialog_rect; 32 ::GetWindowRect(real_dialog, &dialog_rect); 33 34 // Verify that the upper left corner is visible. 35 POINT point = { dialog_rect.left, dialog_rect.top }; 36 HMONITOR monitor1 = ::MonitorFromPoint(point, MONITOR_DEFAULTTONULL); 37 point.x = dialog_rect.right; 38 point.y = dialog_rect.bottom; 39 40 // Verify that the lower right corner is visible. 41 HMONITOR monitor2 = ::MonitorFromPoint(point, MONITOR_DEFAULTTONULL); 42 if (monitor1 && monitor2) 43 return 0; 44 45 // Some part of the dialog box is not visible, fix it by moving is to the 46 // client rect position of the browser window. 47 HWND parent_window = ::GetParent(real_dialog); 48 if (!parent_window) 49 return 0; 50 WINDOWINFO parent_info; 51 parent_info.cbSize = sizeof(WINDOWINFO); 52 ::GetWindowInfo(parent_window, &parent_info); 53 ::SetWindowPos( 54 real_dialog, 55 NULL, 56 parent_info.rcClient.left, 57 parent_info.rcClient.top, 58 0, 59 0, // Size. 60 SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER); 61 62 return 0; 63 } 64 } 65 return 0; 66 } 67 68 } // namespace 69 70 OpenFileName::OpenFileName(HWND parent_window, DWORD flags) { 71 ::ZeroMemory(&openfilename_, sizeof(openfilename_)); 72 openfilename_.lStructSize = sizeof(openfilename_); 73 74 // According to http://support.microsoft.com/?scid=kb;en-us;222003&x=8&y=12, 75 // The lpstrFile Buffer MUST be NULL Terminated. 76 filename_buffer_[0] = 0; 77 openfilename_.lpstrFile = filename_buffer_; 78 openfilename_.nMaxFile = arraysize(filename_buffer_); 79 80 openfilename_.Flags = flags; 81 openfilename_.hwndOwner = parent_window; 82 } 83 84 OpenFileName::~OpenFileName() { 85 } 86 87 void OpenFileName::SetFilters( 88 const std::vector<Tuple2<base::string16, base::string16> >& filters) { 89 openfilename_.lpstrFilter = NULL; 90 filter_buffer_.clear(); 91 if (filters.empty()) 92 return; 93 for (std::vector<Tuple2<base::string16, base::string16> >::const_iterator 94 it = filters.begin(); 95 it != filters.end(); 96 ++it) { 97 filter_buffer_.append(it->a); 98 filter_buffer_.push_back(0); 99 filter_buffer_.append(it->b); 100 filter_buffer_.push_back(0); 101 } 102 filter_buffer_.push_back(0); 103 openfilename_.lpstrFilter = filter_buffer_.c_str(); 104 } 105 106 void OpenFileName::SetInitialSelection(const base::FilePath& initial_directory, 107 const base::FilePath& initial_filename) { 108 // First reset to the default case. 109 // According to http://support.microsoft.com/?scid=kb;en-us;222003&x=8&y=12, 110 // The lpstrFile Buffer MUST be NULL Terminated. 111 filename_buffer_[0] = 0; 112 openfilename_.lpstrFile = filename_buffer_; 113 openfilename_.nMaxFile = arraysize(filename_buffer_); 114 openfilename_.lpstrInitialDir = NULL; 115 initial_directory_buffer_.clear(); 116 117 if (initial_directory.empty()) 118 return; 119 120 initial_directory_buffer_ = initial_directory.value(); 121 openfilename_.lpstrInitialDir = initial_directory_buffer_.c_str(); 122 123 if (initial_filename.empty()) 124 return; 125 126 // The filename is ignored if no initial directory is supplied. 127 base::wcslcpy(filename_buffer_, 128 initial_filename.value().c_str(), 129 arraysize(filename_buffer_)); 130 } 131 132 void OpenFileName::MaybeInstallWindowPositionHookForSaveAsOnXP() { 133 if (base::win::GetVersion() >= base::win::VERSION_VISTA) 134 return; 135 136 openfilename_.Flags |= OFN_ENABLEHOOK; 137 DCHECK(!openfilename_.lpfnHook); 138 openfilename_.lpfnHook = &SaveAsDialogHook; 139 } 140 141 base::FilePath OpenFileName::GetSingleResult() { 142 base::FilePath directory; 143 std::vector<base::FilePath> filenames; 144 GetResult(&directory, &filenames); 145 if (filenames.size() != 1) 146 return base::FilePath(); 147 return directory.Append(filenames[0]); 148 } 149 150 void OpenFileName::GetResult(base::FilePath* directory, 151 std::vector<base::FilePath>* filenames) { 152 DCHECK(filenames->empty()); 153 const wchar_t* selection = openfilename_.lpstrFile; 154 while (*selection) { // Empty string indicates end of list. 155 filenames->push_back(base::FilePath(selection)); 156 // Skip over filename and null-terminator. 157 selection += filenames->back().value().length() + 1; 158 } 159 if (filenames->size() == 1) { 160 // When there is one file, it contains the path and filename. 161 *directory = (*filenames)[0].DirName(); 162 (*filenames)[0] = (*filenames)[0].BaseName(); 163 } else if (filenames->size() > 1) { 164 // Otherwise, the first string is the path, and the remainder are 165 // filenames. 166 *directory = (*filenames)[0]; 167 filenames->erase(filenames->begin()); 168 } 169 } 170 171 // static 172 void OpenFileName::SetResult(const base::FilePath& directory, 173 const std::vector<base::FilePath>& filenames, 174 OPENFILENAME* openfilename) { 175 base::string16 filename_value; 176 if (filenames.size() == 1) { 177 filename_value = directory.Append(filenames[0]).value(); 178 } else { 179 filename_value = directory.value(); 180 filename_value.push_back(0); 181 for (std::vector<base::FilePath>::const_iterator it = filenames.begin(); 182 it != filenames.end(); 183 ++it) { 184 filename_value.append(it->value()); 185 filename_value.push_back(0); 186 } 187 } 188 if (filename_value.size() + 1 < openfilename->nMaxFile) { 189 // Because the result has embedded nulls, we must memcpy. 190 memcpy(openfilename->lpstrFile, 191 filename_value.c_str(), 192 (filename_value.size() + 1) * sizeof(filename_value[0])); 193 } else if (openfilename->nMaxFile) { 194 openfilename->lpstrFile[0] = 0; 195 } 196 } 197 198 // static 199 std::vector<Tuple2<base::string16, base::string16> > OpenFileName::GetFilters( 200 const OPENFILENAME* openfilename) { 201 std::vector<Tuple2<base::string16, base::string16> > filters; 202 203 const base::char16* display_string = openfilename->lpstrFilter; 204 if (!display_string) 205 return filters; 206 207 while (*display_string) { 208 const base::char16* display_string_end = display_string; 209 while (*display_string_end) 210 ++display_string_end; 211 const base::char16* pattern = display_string_end + 1; 212 const base::char16* pattern_end = pattern; 213 while (*pattern_end) 214 ++pattern_end; 215 filters.push_back( 216 MakeTuple(base::string16(display_string, display_string_end), 217 base::string16(pattern, pattern_end))); 218 display_string = pattern_end + 1; 219 } 220 221 return filters; 222 } 223 224 } // namespace win 225 } // namespace ui 226