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
<Tuple2
<base::string16
, base::string16
> >& filters
) {
89 openfilename_
.lpstrFilter
= NULL
;
90 filter_buffer_
.clear();
93 for (std::vector
<Tuple2
<base::string16
, base::string16
> >::const_iterator
97 filter_buffer_
.append(it
->a
);
98 filter_buffer_
.push_back(0);
99 filter_buffer_
.append(it
->b
);
100 filter_buffer_
.push_back(0);
102 filter_buffer_
.push_back(0);
103 openfilename_
.lpstrFilter
= filter_buffer_
.c_str();
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();
117 if (initial_directory
.empty())
120 initial_directory_buffer_
= initial_directory
.value();
121 openfilename_
.lpstrInitialDir
= initial_directory_buffer_
.c_str();
123 if (initial_filename
.empty())
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_
));
132 void OpenFileName::MaybeInstallWindowPositionHookForSaveAsOnXP() {
133 if (base::win::GetVersion() >= base::win::VERSION_VISTA
)
136 openfilename_
.Flags
|= OFN_ENABLEHOOK
;
137 DCHECK(!openfilename_
.lpfnHook
);
138 openfilename_
.lpfnHook
= &SaveAsDialogHook
;
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]);
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;
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
166 *directory
= (*filenames
)[0];
167 filenames
->erase(filenames
->begin());
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();
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();
184 filename_value
.append(it
->value());
185 filename_value
.push_back(0);
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;
199 std::vector
<Tuple2
<base::string16
, base::string16
> > OpenFileName::GetFilters(
200 const OPENFILENAME
* openfilename
) {
201 std::vector
<Tuple2
<base::string16
, base::string16
> > filters
;
203 const base::char16
* display_string
= openfilename
->lpstrFilter
;
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
;
216 MakeTuple(base::string16(display_string
, display_string_end
),
217 base::string16(pattern
, pattern_end
)));
218 display_string
= pattern_end
+ 1;