Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / base / win / open_file_name_win.cc
blob5e5b5b4c5eedd8d86fb7ee8d84ceed232a8c3869
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.
5 #include "ui/base/win/open_file_name_win.h"
7 #include "base/files/file_path.h"
8 #include "base/strings/string_util.h"
9 #include "base/win/windows_version.h"
11 namespace ui {
12 namespace win {
14 namespace {
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;
26 case kPrivateMessage: {
27 // The dialog box is the parent of the current handle.
28 HWND real_dialog = ::GetParent(dialog);
30 // Retrieve the final size.
31 RECT dialog_rect;
32 ::GetWindowRect(real_dialog, &dialog_rect);
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;
40 // Verify that the lower right corner is visible.
41 HMONITOR monitor2 = ::MonitorFromPoint(point, MONITOR_DEFAULTTONULL);
42 if (monitor1 && monitor2)
43 return 0;
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,
59 0, // Size.
60 SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);
62 return 0;
65 return 0;
68 } // namespace
70 OpenFileName::OpenFileName(HWND parent_window, DWORD flags) {
71 ::ZeroMemory(&openfilename_, sizeof(openfilename_));
72 openfilename_.lStructSize = sizeof(openfilename_);
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_);
80 openfilename_.Flags = flags;
81 openfilename_.hwndOwner = parent_window;
84 OpenFileName::~OpenFileName() {
87 void OpenFileName::SetFilters(
88 const std::vector<base::Tuple<base::string16, base::string16>>& filters) {
89 openfilename_.lpstrFilter = NULL;
90 filter_buffer_.clear();
91 if (filters.empty())
92 return;
93 for (const auto& filter : filters) {
94 filter_buffer_.append(base::get<0>(filter));
95 filter_buffer_.push_back(0);
96 filter_buffer_.append(base::get<1>(filter));
97 filter_buffer_.push_back(0);
99 filter_buffer_.push_back(0);
100 openfilename_.lpstrFilter = filter_buffer_.c_str();
103 void OpenFileName::SetInitialSelection(const base::FilePath& initial_directory,
104 const base::FilePath& initial_filename) {
105 // First reset to the default case.
106 // According to http://support.microsoft.com/?scid=kb;en-us;222003&x=8&y=12,
107 // The lpstrFile Buffer MUST be NULL Terminated.
108 filename_buffer_[0] = 0;
109 openfilename_.lpstrFile = filename_buffer_;
110 openfilename_.nMaxFile = arraysize(filename_buffer_);
111 openfilename_.lpstrInitialDir = NULL;
112 initial_directory_buffer_.clear();
114 if (initial_directory.empty())
115 return;
117 initial_directory_buffer_ = initial_directory.value();
118 openfilename_.lpstrInitialDir = initial_directory_buffer_.c_str();
120 if (initial_filename.empty())
121 return;
123 // The filename is ignored if no initial directory is supplied.
124 base::wcslcpy(filename_buffer_,
125 initial_filename.value().c_str(),
126 arraysize(filename_buffer_));
129 void OpenFileName::MaybeInstallWindowPositionHookForSaveAsOnXP() {
130 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
131 return;
133 openfilename_.Flags |= OFN_ENABLEHOOK;
134 DCHECK(!openfilename_.lpfnHook);
135 openfilename_.lpfnHook = &SaveAsDialogHook;
138 base::FilePath OpenFileName::GetSingleResult() {
139 base::FilePath directory;
140 std::vector<base::FilePath> filenames;
141 GetResult(&directory, &filenames);
142 if (filenames.size() != 1)
143 return base::FilePath();
144 return directory.Append(filenames[0]);
147 void OpenFileName::GetResult(base::FilePath* directory,
148 std::vector<base::FilePath>* filenames) {
149 DCHECK(filenames->empty());
150 const wchar_t* selection = openfilename_.lpstrFile;
151 // The return value of |openfilename_.lpstrFile| is dependent on the
152 // value of the Multi-Select flag within |openfilename_|. If the flag is
153 // not set the return value will be a single null-terminated wide string.
154 // If it is set it will be more than one null-terminated wide string, itself
155 // terminated by an empty null-terminated wide string.
156 if (openfilename_.Flags & OFN_ALLOWMULTISELECT) {
157 while (*selection) { // Empty string indicates end of list.
158 filenames->push_back(base::FilePath(selection));
159 // Skip over filename and null-terminator.
160 selection += filenames->back().value().length() + 1;
162 } else {
163 filenames->push_back(base::FilePath(selection));
165 if (filenames->size() == 1) {
166 // When there is one file, it contains the path and filename.
167 *directory = (*filenames)[0].DirName();
168 (*filenames)[0] = (*filenames)[0].BaseName();
169 } else if (filenames->size() > 1) {
170 // Otherwise, the first string is the path, and the remainder are
171 // filenames.
172 *directory = (*filenames)[0];
173 filenames->erase(filenames->begin());
177 // static
178 void OpenFileName::SetResult(const base::FilePath& directory,
179 const std::vector<base::FilePath>& filenames,
180 OPENFILENAME* openfilename) {
181 base::string16 filename_value;
182 if (filenames.size() == 1) {
183 filename_value = directory.Append(filenames[0]).value();
184 } else {
185 filename_value = directory.value();
186 filename_value.push_back(0);
187 for (std::vector<base::FilePath>::const_iterator it = filenames.begin();
188 it != filenames.end();
189 ++it) {
190 filename_value.append(it->value());
191 filename_value.push_back(0);
194 if (filename_value.size() + 1 < openfilename->nMaxFile) {
195 // Because the result has embedded nulls, we must memcpy.
196 memcpy(openfilename->lpstrFile,
197 filename_value.c_str(),
198 (filename_value.size() + 1) * sizeof(filename_value[0]));
199 } else if (openfilename->nMaxFile) {
200 openfilename->lpstrFile[0] = 0;
204 // static
205 std::vector<base::Tuple<base::string16, base::string16>>
206 OpenFileName::GetFilters(const OPENFILENAME* openfilename) {
207 std::vector<base::Tuple<base::string16, base::string16>> filters;
209 const base::char16* display_string = openfilename->lpstrFilter;
210 if (!display_string)
211 return filters;
213 while (*display_string) {
214 const base::char16* display_string_end = display_string;
215 while (*display_string_end)
216 ++display_string_end;
217 const base::char16* pattern = display_string_end + 1;
218 const base::char16* pattern_end = pattern;
219 while (*pattern_end)
220 ++pattern_end;
221 filters.push_back(
222 base::MakeTuple(base::string16(display_string, display_string_end),
223 base::string16(pattern, pattern_end)));
224 display_string = pattern_end + 1;
227 return filters;
230 } // namespace win
231 } // namespace ui