blob: 3914fa5cfae43951dc8ace26a60bf10565df7345 [file] [log] [blame]
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/win/open_file_name_win.h"
#include "base/files/file_path.h"
#include "base/strings/string_util.h"
#include "base/win/windows_version.h"
namespace ui {
namespace win {
namespace {
// Ensures that the Save As dialog is on-screen.
UINT_PTR CALLBACK SaveAsDialogHook(HWND dialog, UINT message,
WPARAM wparam, LPARAM lparam) {
static const UINT kPrivateMessage = 0x2F3F;
switch (message) {
case WM_INITDIALOG: {
// Do nothing here. Just post a message to defer actual processing.
::PostMessage(dialog, kPrivateMessage, 0, 0);
return TRUE;
}
case kPrivateMessage: {
// The dialog box is the parent of the current handle.
HWND real_dialog = ::GetParent(dialog);
// Retrieve the final size.
RECT dialog_rect;
::GetWindowRect(real_dialog, &dialog_rect);
// Verify that the upper left corner is visible.
POINT point = { dialog_rect.left, dialog_rect.top };
HMONITOR monitor1 = ::MonitorFromPoint(point, MONITOR_DEFAULTTONULL);
point.x = dialog_rect.right;
point.y = dialog_rect.bottom;
// Verify that the lower right corner is visible.
HMONITOR monitor2 = ::MonitorFromPoint(point, MONITOR_DEFAULTTONULL);
if (monitor1 && monitor2)
return 0;
// Some part of the dialog box is not visible, fix it by moving is to the
// client rect position of the browser window.
HWND parent_window = ::GetParent(real_dialog);
if (!parent_window)
return 0;
WINDOWINFO parent_info;
parent_info.cbSize = sizeof(WINDOWINFO);
::GetWindowInfo(parent_window, &parent_info);
::SetWindowPos(
real_dialog,
NULL,
parent_info.rcClient.left,
parent_info.rcClient.top,
0,
0, // Size.
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);
return 0;
}
}
return 0;
}
} // namespace
OpenFileName::OpenFileName(HWND parent_window, DWORD flags) {
::ZeroMemory(&openfilename_, sizeof(openfilename_));
openfilename_.lStructSize = sizeof(openfilename_);
// According to http://support.microsoft.com/?scid=kb;en-us;222003&x=8&y=12,
// The lpstrFile Buffer MUST be NULL Terminated.
filename_buffer_[0] = 0;
openfilename_.lpstrFile = filename_buffer_;
openfilename_.nMaxFile = arraysize(filename_buffer_);
openfilename_.Flags = flags;
openfilename_.hwndOwner = parent_window;
}
OpenFileName::~OpenFileName() {
}
void OpenFileName::SetFilters(
const std::vector<Tuple2<base::string16, base::string16> >& filters) {
openfilename_.lpstrFilter = NULL;
filter_buffer_.clear();
if (filters.empty())
return;
for (std::vector<Tuple2<base::string16, base::string16> >::const_iterator
it = filters.begin();
it != filters.end();
++it) {
filter_buffer_.append(it->a);
filter_buffer_.push_back(0);
filter_buffer_.append(it->b);
filter_buffer_.push_back(0);
}
filter_buffer_.push_back(0);
openfilename_.lpstrFilter = filter_buffer_.c_str();
}
void OpenFileName::SetInitialSelection(const base::FilePath& initial_directory,
const base::FilePath& initial_filename) {
// First reset to the default case.
// According to http://support.microsoft.com/?scid=kb;en-us;222003&x=8&y=12,
// The lpstrFile Buffer MUST be NULL Terminated.
filename_buffer_[0] = 0;
openfilename_.lpstrFile = filename_buffer_;
openfilename_.nMaxFile = arraysize(filename_buffer_);
openfilename_.lpstrInitialDir = NULL;
initial_directory_buffer_.clear();
if (initial_directory.empty())
return;
initial_directory_buffer_ = initial_directory.value();
openfilename_.lpstrInitialDir = initial_directory_buffer_.c_str();
if (initial_filename.empty())
return;
// The filename is ignored if no initial directory is supplied.
base::wcslcpy(filename_buffer_,
initial_filename.value().c_str(),
arraysize(filename_buffer_));
}
void OpenFileName::MaybeInstallWindowPositionHookForSaveAsOnXP() {
if (base::win::GetVersion() >= base::win::VERSION_VISTA)
return;
openfilename_.Flags |= OFN_ENABLEHOOK;
DCHECK(!openfilename_.lpfnHook);
openfilename_.lpfnHook = &SaveAsDialogHook;
}
base::FilePath OpenFileName::GetSingleResult() {
base::FilePath directory;
std::vector<base::FilePath> filenames;
GetResult(&directory, &filenames);
if (filenames.size() != 1)
return base::FilePath();
return directory.Append(filenames[0]);
}
void OpenFileName::GetResult(base::FilePath* directory,
std::vector<base::FilePath>* filenames) {
DCHECK(filenames->empty());
const wchar_t* selection = openfilename_.lpstrFile;
// The return value of |openfilename_.lpstrFile| is dependent on the
// value of the Multi-Select flag within |openfilename_|. If the flag is
// not set the return value will be a single null-terminated wide string.
// If it is set it will be more than one null-terminated wide string, itself
// terminated by an empty null-terminated wide string.
if (openfilename_.Flags & OFN_ALLOWMULTISELECT) {
while (*selection) { // Empty string indicates end of list.
filenames->push_back(base::FilePath(selection));
// Skip over filename and null-terminator.
selection += filenames->back().value().length() + 1;
}
} else {
filenames->push_back(base::FilePath(selection));
}
if (filenames->size() == 1) {
// When there is one file, it contains the path and filename.
*directory = (*filenames)[0].DirName();
(*filenames)[0] = (*filenames)[0].BaseName();
} else if (filenames->size() > 1) {
// Otherwise, the first string is the path, and the remainder are
// filenames.
*directory = (*filenames)[0];
filenames->erase(filenames->begin());
}
}
// static
void OpenFileName::SetResult(const base::FilePath& directory,
const std::vector<base::FilePath>& filenames,
OPENFILENAME* openfilename) {
base::string16 filename_value;
if (filenames.size() == 1) {
filename_value = directory.Append(filenames[0]).value();
} else {
filename_value = directory.value();
filename_value.push_back(0);
for (std::vector<base::FilePath>::const_iterator it = filenames.begin();
it != filenames.end();
++it) {
filename_value.append(it->value());
filename_value.push_back(0);
}
}
if (filename_value.size() + 1 < openfilename->nMaxFile) {
// Because the result has embedded nulls, we must memcpy.
memcpy(openfilename->lpstrFile,
filename_value.c_str(),
(filename_value.size() + 1) * sizeof(filename_value[0]));
} else if (openfilename->nMaxFile) {
openfilename->lpstrFile[0] = 0;
}
}
// static
std::vector<Tuple2<base::string16, base::string16> > OpenFileName::GetFilters(
const OPENFILENAME* openfilename) {
std::vector<Tuple2<base::string16, base::string16> > filters;
const base::char16* display_string = openfilename->lpstrFilter;
if (!display_string)
return filters;
while (*display_string) {
const base::char16* display_string_end = display_string;
while (*display_string_end)
++display_string_end;
const base::char16* pattern = display_string_end + 1;
const base::char16* pattern_end = pattern;
while (*pattern_end)
++pattern_end;
filters.push_back(
MakeTuple(base::string16(display_string, display_string_end),
base::string16(pattern, pattern_end)));
display_string = pattern_end + 1;
}
return filters;
}
} // namespace win
} // namespace ui