• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
SaveAsDialogHook(HWND dialog,UINT message,WPARAM wparam,LPARAM lparam)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 
OpenFileName(HWND parent_window,DWORD flags)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 
~OpenFileName()84 OpenFileName::~OpenFileName() {
85 }
86 
SetFilters(const std::vector<Tuple2<base::string16,base::string16>> & filters)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 
SetInitialSelection(const base::FilePath & initial_directory,const base::FilePath & initial_filename)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 
MaybeInstallWindowPositionHookForSaveAsOnXP()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 
GetSingleResult()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 
GetResult(base::FilePath * directory,std::vector<base::FilePath> * filenames)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
SetResult(const base::FilePath & directory,const std::vector<base::FilePath> & filenames,OPENFILENAME * openfilename)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
GetFilters(const OPENFILENAME * openfilename)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