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"
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;
22 // Do nothing here. Just post a message to defer actual processing.
23 ::PostMessage(dialog
, kPrivateMessage
, 0, 0);
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.
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
)
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
);
50 WINDOWINFO parent_info
;
51 parent_info
.cbSize
= sizeof(WINDOWINFO
);
52 ::GetWindowInfo(parent_window
, &parent_info
);
56 parent_info
.rcClient
.left
,
57 parent_info
.rcClient
.top
,
60 SWP_NOACTIVATE
| SWP_NOOWNERZORDER
| SWP_NOSIZE
| SWP_NOZORDER
);
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();
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())
117 initial_directory_buffer_
= initial_directory
.value();
118 openfilename_
.lpstrInitialDir
= initial_directory_buffer_
.c_str();
120 if (initial_filename
.empty())
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
)
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;
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
172 *directory
= (*filenames
)[0];
173 filenames
->erase(filenames
->begin());
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();
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();
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;
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
;
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
;
222 base::MakeTuple(base::string16(display_string
, display_string_end
),
223 base::string16(pattern
, pattern_end
)));
224 display_string
= pattern_end
+ 1;