Home | History | Annotate | Download | only in win
      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.
     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 
     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 
     84 OpenFileName::~OpenFileName() {
     85 }
     86 
     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 
    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 
    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 
    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 
    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
    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
    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