| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2009 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 "chrome/common/win_util.h" | |
| 6 | |
| 7 #include <atlbase.h> | |
| 8 #include <atlapp.h> | |
| 9 #include <commdlg.h> | |
| 10 #include <dwmapi.h> | |
| 11 #include <shellapi.h> | |
| 12 #include <shlobj.h> | |
| 13 | |
| 14 #include "app/l10n_util.h" | |
| 15 #include "app/l10n_util_win.h" | |
| 16 #include "base/file_util.h" | |
| 17 #include "base/gfx/gdi_util.h" | |
| 18 #include "base/gfx/png_encoder.h" | |
| 19 #include "base/logging.h" | |
| 20 #include "base/registry.h" | |
| 21 #include "base/scoped_handle.h" | |
| 22 #include "base/string_util.h" | |
| 23 #include "base/win_util.h" | |
| 24 #include "grit/generated_resources.h" | |
| 25 #include "net/base/mime_util.h" | |
| 26 | |
| 27 // Ensure that we pick up this link library. | |
| 28 #pragma comment(lib, "dwmapi.lib") | |
| 29 | |
| 30 namespace win_util { | |
| 31 | |
| 32 const int kAutoHideTaskbarThicknessPx = 2; | |
| 33 | |
| 34 namespace { | |
| 35 | |
| 36 // Enforce visible dialog box. | |
| 37 UINT_PTR CALLBACK SaveAsDialogHook(HWND dialog, UINT message, | |
| 38 WPARAM wparam, LPARAM lparam) { | |
| 39 static const UINT kPrivateMessage = 0x2F3F; | |
| 40 switch (message) { | |
| 41 case WM_INITDIALOG: { | |
| 42 // Do nothing here. Just post a message to defer actual processing. | |
| 43 PostMessage(dialog, kPrivateMessage, 0, 0); | |
| 44 return TRUE; | |
| 45 } | |
| 46 case kPrivateMessage: { | |
| 47 // The dialog box is the parent of the current handle. | |
| 48 HWND real_dialog = GetParent(dialog); | |
| 49 | |
| 50 // Retrieve the final size. | |
| 51 RECT dialog_rect; | |
| 52 GetWindowRect(real_dialog, &dialog_rect); | |
| 53 | |
| 54 // Verify that the upper left corner is visible. | |
| 55 POINT point = { dialog_rect.left, dialog_rect.top }; | |
| 56 HMONITOR monitor1 = MonitorFromPoint(point, MONITOR_DEFAULTTONULL); | |
| 57 point.x = dialog_rect.right; | |
| 58 point.y = dialog_rect.bottom; | |
| 59 | |
| 60 // Verify that the lower right corner is visible. | |
| 61 HMONITOR monitor2 = MonitorFromPoint(point, MONITOR_DEFAULTTONULL); | |
| 62 if (monitor1 && monitor2) | |
| 63 return 0; | |
| 64 | |
| 65 // Some part of the dialog box is not visible, fix it by moving is to the | |
| 66 // client rect position of the browser window. | |
| 67 HWND parent_window = GetParent(real_dialog); | |
| 68 if (!parent_window) | |
| 69 return 0; | |
| 70 WINDOWINFO parent_info; | |
| 71 parent_info.cbSize = sizeof(WINDOWINFO); | |
| 72 GetWindowInfo(parent_window, &parent_info); | |
| 73 SetWindowPos(real_dialog, NULL, | |
| 74 parent_info.rcClient.left, | |
| 75 parent_info.rcClient.top, | |
| 76 0, 0, // Size. | |
| 77 SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | | |
| 78 SWP_NOZORDER); | |
| 79 | |
| 80 return 0; | |
| 81 } | |
| 82 } | |
| 83 return 0; | |
| 84 } | |
| 85 | |
| 86 } // namespace | |
| 87 | |
| 88 std::wstring FormatSystemTime(const SYSTEMTIME& time, | |
| 89 const std::wstring& format) { | |
| 90 // If the format string is empty, just use the default format. | |
| 91 LPCTSTR format_ptr = NULL; | |
| 92 if (!format.empty()) | |
| 93 format_ptr = format.c_str(); | |
| 94 | |
| 95 int buffer_size = GetTimeFormat(LOCALE_USER_DEFAULT, NULL, &time, format_ptr, | |
| 96 NULL, 0); | |
| 97 | |
| 98 std::wstring output; | |
| 99 GetTimeFormat(LOCALE_USER_DEFAULT, NULL, &time, format_ptr, | |
| 100 WriteInto(&output, buffer_size), buffer_size); | |
| 101 | |
| 102 return output; | |
| 103 } | |
| 104 | |
| 105 std::wstring FormatSystemDate(const SYSTEMTIME& date, | |
| 106 const std::wstring& format) { | |
| 107 // If the format string is empty, just use the default format. | |
| 108 LPCTSTR format_ptr = NULL; | |
| 109 if (!format.empty()) | |
| 110 format_ptr = format.c_str(); | |
| 111 | |
| 112 int buffer_size = GetDateFormat(LOCALE_USER_DEFAULT, NULL, &date, format_ptr, | |
| 113 NULL, 0); | |
| 114 | |
| 115 std::wstring output; | |
| 116 GetDateFormat(LOCALE_USER_DEFAULT, NULL, &date, format_ptr, | |
| 117 WriteInto(&output, buffer_size), buffer_size); | |
| 118 | |
| 119 return output; | |
| 120 } | |
| 121 | |
| 122 bool ConvertToLongPath(const std::wstring& short_path, | |
| 123 std::wstring* long_path) { | |
| 124 wchar_t long_path_buf[MAX_PATH]; | |
| 125 DWORD return_value = GetLongPathName(short_path.c_str(), long_path_buf, | |
| 126 MAX_PATH); | |
| 127 if (return_value != 0 && return_value < MAX_PATH) { | |
| 128 *long_path = long_path_buf; | |
| 129 return true; | |
| 130 } | |
| 131 | |
| 132 return false; | |
| 133 } | |
| 134 | |
| 135 bool IsDoubleClick(const POINT& origin, | |
| 136 const POINT& current, | |
| 137 DWORD elapsed_time) { | |
| 138 // The CXDOUBLECLK and CYDOUBLECLK system metrics describe the width and | |
| 139 // height of a rectangle around the origin position, inside of which clicks | |
| 140 // within the double click time are considered double clicks. | |
| 141 return (elapsed_time <= GetDoubleClickTime()) && | |
| 142 (abs(current.x - origin.x) <= (GetSystemMetrics(SM_CXDOUBLECLK) / 2)) && | |
| 143 (abs(current.y - origin.y) <= (GetSystemMetrics(SM_CYDOUBLECLK) / 2)); | |
| 144 } | |
| 145 | |
| 146 bool IsDrag(const POINT& origin, const POINT& current) { | |
| 147 // The CXDRAG and CYDRAG system metrics describe the width and height of a | |
| 148 // rectangle around the origin position, inside of which motion is not | |
| 149 // considered a drag. | |
| 150 return (abs(current.x - origin.x) > (GetSystemMetrics(SM_CXDRAG) / 2)) || | |
| 151 (abs(current.y - origin.y) > (GetSystemMetrics(SM_CYDRAG) / 2)); | |
| 152 } | |
| 153 | |
| 154 bool ShouldUseVistaFrame() { | |
| 155 if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) | |
| 156 return false; | |
| 157 // If composition is not enabled, we behave like on XP. | |
| 158 BOOL f; | |
| 159 DwmIsCompositionEnabled(&f); | |
| 160 return !!f; | |
| 161 } | |
| 162 | |
| 163 // Open an item via a shell execute command. Error code checking and casting | |
| 164 // explanation: http://msdn2.microsoft.com/en-us/library/ms647732.aspx | |
| 165 bool OpenItemViaShell(const FilePath& full_path, bool ask_for_app) { | |
| 166 HINSTANCE h = ::ShellExecuteW( | |
| 167 NULL, NULL, full_path.value().c_str(), NULL, | |
| 168 full_path.DirName().value().c_str(), SW_SHOWNORMAL); | |
| 169 | |
| 170 LONG_PTR error = reinterpret_cast<LONG_PTR>(h); | |
| 171 if (error > 32) | |
| 172 return true; | |
| 173 | |
| 174 if ((error == SE_ERR_NOASSOC) && ask_for_app) | |
| 175 return OpenItemWithExternalApp(full_path.value()); | |
| 176 | |
| 177 return false; | |
| 178 } | |
| 179 | |
| 180 bool OpenItemViaShellNoZoneCheck(const FilePath& full_path, | |
| 181 bool ask_for_app) { | |
| 182 SHELLEXECUTEINFO sei = { sizeof(sei) }; | |
| 183 sei.fMask = SEE_MASK_NOZONECHECKS | SEE_MASK_FLAG_DDEWAIT; | |
| 184 sei.nShow = SW_SHOWNORMAL; | |
| 185 sei.lpVerb = NULL; | |
| 186 sei.lpFile = full_path.value().c_str(); | |
| 187 if (::ShellExecuteExW(&sei)) | |
| 188 return true; | |
| 189 LONG_PTR error = reinterpret_cast<LONG_PTR>(sei.hInstApp); | |
| 190 if ((error == SE_ERR_NOASSOC) && ask_for_app) | |
| 191 return OpenItemWithExternalApp(full_path.value()); | |
| 192 return false; | |
| 193 } | |
| 194 | |
| 195 // Show the Windows "Open With" dialog box to ask the user to pick an app to | |
| 196 // open the file with. | |
| 197 bool OpenItemWithExternalApp(const std::wstring& full_path) { | |
| 198 SHELLEXECUTEINFO sei = { sizeof(sei) }; | |
| 199 sei.fMask = SEE_MASK_FLAG_DDEWAIT; | |
| 200 sei.nShow = SW_SHOWNORMAL; | |
| 201 sei.lpVerb = L"openas"; | |
| 202 sei.lpFile = full_path.c_str(); | |
| 203 return (TRUE == ::ShellExecuteExW(&sei)); | |
| 204 } | |
| 205 | |
| 206 // Get the file type description from the registry. This will be "Text Document" | |
| 207 // for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't | |
| 208 // have an entry for the file type, we return false, true if the description was | |
| 209 // found. 'file_ext' must be in form ".txt". | |
| 210 static bool GetRegistryDescriptionFromExtension(const std::wstring& file_ext, | |
| 211 std::wstring* reg_description) { | |
| 212 DCHECK(reg_description); | |
| 213 RegKey reg_ext(HKEY_CLASSES_ROOT, file_ext.c_str(), KEY_READ); | |
| 214 std::wstring reg_app; | |
| 215 if (reg_ext.ReadValue(NULL, ®_app) && !reg_app.empty()) { | |
| 216 RegKey reg_link(HKEY_CLASSES_ROOT, reg_app.c_str(), KEY_READ); | |
| 217 if (reg_link.ReadValue(NULL, reg_description)) | |
| 218 return true; | |
| 219 } | |
| 220 return false; | |
| 221 } | |
| 222 | |
| 223 std::wstring FormatFilterForExtensions( | |
| 224 const std::vector<std::wstring>& file_ext, | |
| 225 const std::vector<std::wstring>& ext_desc, | |
| 226 bool include_all_files) { | |
| 227 const std::wstring all_ext = L"*.*"; | |
| 228 const std::wstring all_desc = l10n_util::GetString(IDS_SAVEAS_ALL_FILES); | |
| 229 | |
| 230 DCHECK(file_ext.size()>=ext_desc.size()); | |
| 231 | |
| 232 std::wstring result; | |
| 233 | |
| 234 for (size_t i=0; i<file_ext.size(); ++i) { | |
| 235 std::wstring ext = file_ext[i]; | |
| 236 std::wstring desc; | |
| 237 if (i<ext_desc.size()) | |
| 238 desc = ext_desc[i]; | |
| 239 | |
| 240 if (ext.empty()) { | |
| 241 // Force something reasonable to appear in the dialog box if there is no | |
| 242 // extension provided. | |
| 243 include_all_files = true; | |
| 244 continue; | |
| 245 } | |
| 246 | |
| 247 if (desc.empty()) { | |
| 248 DCHECK(ext.find(L'.') != std::wstring::npos); | |
| 249 std::wstring first_extension = ext.substr(ext.find(L'.')); | |
| 250 size_t first_separator_index = first_extension.find(L';'); | |
| 251 if (first_separator_index != std::wstring::npos) | |
| 252 first_extension = first_extension.substr(0, first_separator_index); | |
| 253 if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) { | |
| 254 // The extension doesn't exist in the registry. It's likely bogus, so | |
| 255 // just drop it. | |
| 256 include_all_files = true; | |
| 257 continue; | |
| 258 } | |
| 259 if (desc.empty()) | |
| 260 desc = L"*." + first_extension; | |
| 261 } | |
| 262 | |
| 263 result.append(desc.c_str(), desc.size()+1); // Append NULL too. | |
| 264 result.append(ext.c_str(), ext.size()+1); | |
| 265 } | |
| 266 | |
| 267 if (include_all_files) { | |
| 268 result.append(all_desc.c_str(), all_desc.size()+1); | |
| 269 result.append(all_ext.c_str(), all_ext.size()+1); | |
| 270 } | |
| 271 | |
| 272 result.append(1, '\0'); // Double NULL required. | |
| 273 return result; | |
| 274 } | |
| 275 | |
| 276 bool SaveFileAs(HWND owner, | |
| 277 const std::wstring& suggested_name, | |
| 278 std::wstring* final_name) { | |
| 279 std::wstring file_ext = file_util::GetFileExtensionFromPath(suggested_name); | |
| 280 file_ext.insert(0, L"*."); | |
| 281 std::wstring filter = FormatFilterForExtensions( | |
| 282 std::vector<std::wstring>(1, file_ext), | |
| 283 std::vector<std::wstring>(), | |
| 284 true); | |
| 285 unsigned index = 1; | |
| 286 return SaveFileAsWithFilter(owner, | |
| 287 suggested_name, | |
| 288 filter, | |
| 289 L"", | |
| 290 false, | |
| 291 &index, | |
| 292 final_name); | |
| 293 } | |
| 294 | |
| 295 bool SaveFileAsWithFilter(HWND owner, | |
| 296 const std::wstring& suggested_name, | |
| 297 const std::wstring& filter, | |
| 298 const std::wstring& def_ext, | |
| 299 bool ignore_suggested_ext, | |
| 300 unsigned* index, | |
| 301 std::wstring* final_name) { | |
| 302 DCHECK(final_name); | |
| 303 // Having an empty filter makes for a bad user experience. We should always | |
| 304 // specify a filter when saving. | |
| 305 DCHECK(!filter.empty()); | |
| 306 std::wstring file_part = file_util::GetFilenameFromPath(suggested_name); | |
| 307 | |
| 308 // The size of the in/out buffer in number of characters we pass to win32 | |
| 309 // GetSaveFileName. From MSDN "The buffer must be large enough to store the | |
| 310 // path and file name string or strings, including the terminating NULL | |
| 311 // character. ... The buffer should be at least 256 characters long.". | |
| 312 // _IsValidPathComDlg does a copy expecting at most MAX_PATH, otherwise will | |
| 313 // result in an error of FNERR_INVALIDFILENAME. So we should only pass the | |
| 314 // API a buffer of at most MAX_PATH. | |
| 315 wchar_t file_name[MAX_PATH]; | |
| 316 base::wcslcpy(file_name, file_part.c_str(), arraysize(file_name)); | |
| 317 | |
| 318 OPENFILENAME save_as; | |
| 319 // We must do this otherwise the ofn's FlagsEx may be initialized to random | |
| 320 // junk in release builds which can cause the Places Bar not to show up! | |
| 321 ZeroMemory(&save_as, sizeof(save_as)); | |
| 322 save_as.lStructSize = sizeof(OPENFILENAME); | |
| 323 save_as.hwndOwner = owner; | |
| 324 save_as.hInstance = NULL; | |
| 325 | |
| 326 save_as.lpstrFilter = filter.empty() ? NULL : filter.c_str(); | |
| 327 | |
| 328 save_as.lpstrCustomFilter = NULL; | |
| 329 save_as.nMaxCustFilter = 0; | |
| 330 save_as.nFilterIndex = *index; | |
| 331 save_as.lpstrFile = file_name; | |
| 332 save_as.nMaxFile = arraysize(file_name); | |
| 333 save_as.lpstrFileTitle = NULL; | |
| 334 save_as.nMaxFileTitle = 0; | |
| 335 | |
| 336 // Set up the initial directory for the dialog. | |
| 337 std::wstring directory = file_util::GetDirectoryFromPath(suggested_name); | |
| 338 save_as.lpstrInitialDir = directory.c_str(); | |
| 339 save_as.lpstrTitle = NULL; | |
| 340 save_as.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLESIZING | | |
| 341 OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; | |
| 342 save_as.lpstrDefExt = &def_ext[0]; | |
| 343 save_as.lCustData = NULL; | |
| 344 | |
| 345 if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) { | |
| 346 // The save as on Windows XP remembers its last position, | |
| 347 // and if the screen resolution changed, it will be off screen. | |
| 348 save_as.Flags |= OFN_ENABLEHOOK; | |
| 349 save_as.lpfnHook = &SaveAsDialogHook; | |
| 350 } | |
| 351 | |
| 352 // Must be NULL or 0. | |
| 353 save_as.pvReserved = NULL; | |
| 354 save_as.dwReserved = 0; | |
| 355 | |
| 356 if (!GetSaveFileName(&save_as)) { | |
| 357 // Zero means the dialog was closed, otherwise we had an error. | |
| 358 DWORD error_code = CommDlgExtendedError(); | |
| 359 if (error_code != 0) { | |
| 360 NOTREACHED() << "GetSaveFileName failed with code: " << error_code; | |
| 361 } | |
| 362 return false; | |
| 363 } | |
| 364 | |
| 365 // Return the user's choice. | |
| 366 final_name->assign(save_as.lpstrFile); | |
| 367 *index = save_as.nFilterIndex; | |
| 368 | |
| 369 // Figure out what filter got selected from the vector with embedded nulls. | |
| 370 // NOTE: The filter contains a string with embedded nulls, such as: | |
| 371 // JPG Image\0*.jpg\0All files\0*.*\0\0 | |
| 372 // The filter index is 1-based index for which pair got selected. So, using | |
| 373 // the example above, if the first index was selected we need to skip 1 | |
| 374 // instance of null to get to "*.jpg". | |
| 375 std::vector<std::wstring> filters; | |
| 376 if (!filter.empty() && save_as.nFilterIndex > 0) | |
| 377 SplitString(filter, '\0', &filters); | |
| 378 std::wstring filter_selected; | |
| 379 if (!filters.empty()) | |
| 380 filter_selected = filters[(2 * (save_as.nFilterIndex - 1)) + 1]; | |
| 381 | |
| 382 // Get the extension that was suggested to the user (when the Save As dialog | |
| 383 // was opened). For saving web pages, we skip this step since there may be | |
| 384 // 'extension characters' in the title of the web page. | |
| 385 std::wstring suggested_ext; | |
| 386 if (!ignore_suggested_ext) | |
| 387 suggested_ext = file_util::GetFileExtensionFromPath(suggested_name); | |
| 388 | |
| 389 // If we can't get the extension from the suggested_name, we use the default | |
| 390 // extension passed in. This is to cover cases like when saving a web page, | |
| 391 // where we get passed in a name without an extension and a default extension | |
| 392 // along with it. | |
| 393 if (suggested_ext.empty()) | |
| 394 suggested_ext = def_ext; | |
| 395 | |
| 396 *final_name = | |
| 397 AppendExtensionIfNeeded(*final_name, filter_selected, suggested_ext); | |
| 398 return true; | |
| 399 } | |
| 400 | |
| 401 std::wstring AppendExtensionIfNeeded(const std::wstring& filename, | |
| 402 const std::wstring& filter_selected, | |
| 403 const std::wstring& suggested_ext) { | |
| 404 std::wstring return_value = filename; | |
| 405 | |
| 406 // Get the extension the user ended up selecting. | |
| 407 std::wstring selected_ext = file_util::GetFileExtensionFromPath(filename); | |
| 408 | |
| 409 if (filter_selected.empty() || filter_selected == L"*.*") { | |
| 410 // If the user selects 'All files' we respect any extension given to us from | |
| 411 // the File Save dialog. We also strip any trailing dots, which matches | |
| 412 // Windows Explorer and is needed because Windows doesn't allow filenames | |
| 413 // to have trailing dots. The GetSaveFileName dialog will not return a | |
| 414 // string with only one or more dots. | |
| 415 size_t index = return_value.find_last_not_of(L'.'); | |
| 416 if (index < return_value.size() - 1) | |
| 417 return_value.resize(index + 1); | |
| 418 } else { | |
| 419 // User selected a specific filter (not *.*) so we need to check if the | |
| 420 // extension provided has the same mime type. If it doesn't we append the | |
| 421 // extension. | |
| 422 std::string suggested_mime_type, selected_mime_type; | |
| 423 if (suggested_ext != selected_ext && | |
| 424 (!net::GetMimeTypeFromExtension(suggested_ext, &suggested_mime_type) || | |
| 425 !net::GetMimeTypeFromExtension(selected_ext, &selected_mime_type) || | |
| 426 suggested_mime_type != selected_mime_type)) { | |
| 427 return_value.append(L"."); | |
| 428 return_value.append(suggested_ext); | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 return return_value; | |
| 433 } | |
| 434 | |
| 435 // Adjust the window to fit, returning true if the window was resized or moved. | |
| 436 static bool AdjustWindowToFit(HWND hwnd, const RECT& bounds) { | |
| 437 // Get the monitor. | |
| 438 HMONITOR hmon = MonitorFromRect(&bounds, MONITOR_DEFAULTTONEAREST); | |
| 439 if (!hmon) { | |
| 440 NOTREACHED() << "Unable to find default monitor"; | |
| 441 // No monitor available. | |
| 442 return false; | |
| 443 } | |
| 444 | |
| 445 MONITORINFO mi; | |
| 446 mi.cbSize = sizeof(mi); | |
| 447 GetMonitorInfo(hmon, &mi); | |
| 448 gfx::Rect window_rect(bounds); | |
| 449 gfx::Rect monitor_rect(mi.rcWork); | |
| 450 gfx::Rect new_window_rect = window_rect.AdjustToFit(monitor_rect); | |
| 451 if (!new_window_rect.Equals(window_rect)) { | |
| 452 // Window doesn't fit on monitor, move and possibly resize. | |
| 453 SetWindowPos(hwnd, 0, new_window_rect.x(), new_window_rect.y(), | |
| 454 new_window_rect.width(), new_window_rect.height(), | |
| 455 SWP_NOACTIVATE | SWP_NOZORDER); | |
| 456 return true; | |
| 457 } else { | |
| 458 return false; | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 void AdjustWindowToFit(HWND hwnd) { | |
| 463 // Get the window bounds. | |
| 464 RECT r; | |
| 465 GetWindowRect(hwnd, &r); | |
| 466 AdjustWindowToFit(hwnd, r); | |
| 467 } | |
| 468 | |
| 469 void CenterAndSizeWindow(HWND parent, HWND window, const SIZE& pref, | |
| 470 bool pref_is_client) { | |
| 471 DCHECK(window && pref.cx > 0 && pref.cy > 0); | |
| 472 // Calculate the ideal bounds. | |
| 473 RECT window_bounds; | |
| 474 RECT center_bounds = {0}; | |
| 475 if (parent) { | |
| 476 // If there is a parent, center over the parents bounds. | |
| 477 ::GetWindowRect(parent, ¢er_bounds); | |
| 478 } else { | |
| 479 // No parent. Center over the monitor the window is on. | |
| 480 HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST); | |
| 481 if (monitor) { | |
| 482 MONITORINFO mi = {0}; | |
| 483 mi.cbSize = sizeof(mi); | |
| 484 GetMonitorInfo(monitor, &mi); | |
| 485 center_bounds = mi.rcWork; | |
| 486 } else { | |
| 487 NOTREACHED() << "Unable to get default monitor"; | |
| 488 } | |
| 489 } | |
| 490 window_bounds.left = center_bounds.left + | |
| 491 (center_bounds.right - center_bounds.left - pref.cx) / 2; | |
| 492 window_bounds.right = window_bounds.left + pref.cx; | |
| 493 window_bounds.top = center_bounds.top + | |
| 494 (center_bounds.bottom - center_bounds.top - pref.cy) / 2; | |
| 495 window_bounds.bottom = window_bounds.top + pref.cy; | |
| 496 | |
| 497 // If we're centering a child window, we are positioning in client | |
| 498 // coordinates, and as such we need to offset the target rectangle by the | |
| 499 // position of the parent window. | |
| 500 if (::GetWindowLong(window, GWL_STYLE) & WS_CHILD) { | |
| 501 DCHECK(parent && ::GetParent(window) == parent); | |
| 502 POINT topleft = { window_bounds.left, window_bounds.top }; | |
| 503 ::MapWindowPoints(HWND_DESKTOP, parent, &topleft, 1); | |
| 504 window_bounds.left = topleft.x; | |
| 505 window_bounds.top = topleft.y; | |
| 506 window_bounds.right = window_bounds.left + pref.cx; | |
| 507 window_bounds.bottom = window_bounds.top + pref.cy; | |
| 508 } | |
| 509 | |
| 510 // Get the WINDOWINFO for window. We need the style to calculate the size | |
| 511 // for the window. | |
| 512 WINDOWINFO win_info = {0}; | |
| 513 win_info.cbSize = sizeof(WINDOWINFO); | |
| 514 GetWindowInfo(window, &win_info); | |
| 515 | |
| 516 // Calculate the window size needed for the content size. | |
| 517 | |
| 518 if (!pref_is_client || | |
| 519 AdjustWindowRectEx(&window_bounds, win_info.dwStyle, FALSE, | |
| 520 win_info.dwExStyle)) { | |
| 521 if (!AdjustWindowToFit(window, window_bounds)) { | |
| 522 // The window fits, reset the bounds. | |
| 523 SetWindowPos(window, 0, window_bounds.left, window_bounds.top, | |
| 524 window_bounds.right - window_bounds.left, | |
| 525 window_bounds.bottom - window_bounds.top, | |
| 526 SWP_NOACTIVATE | SWP_NOZORDER); | |
| 527 } // else case, AdjustWindowToFit set the bounds for us. | |
| 528 } else { | |
| 529 NOTREACHED() << "Unable to adjust window to fit"; | |
| 530 } | |
| 531 } | |
| 532 | |
| 533 bool EdgeHasTopmostAutoHideTaskbar(UINT edge, HMONITOR monitor) { | |
| 534 APPBARDATA taskbar_data = { 0 }; | |
| 535 taskbar_data.cbSize = sizeof APPBARDATA; | |
| 536 taskbar_data.uEdge = edge; | |
| 537 HWND taskbar = reinterpret_cast<HWND>(SHAppBarMessage(ABM_GETAUTOHIDEBAR, | |
| 538 &taskbar_data)); | |
| 539 return ::IsWindow(taskbar) && (monitor != NULL) && | |
| 540 (MonitorFromWindow(taskbar, MONITOR_DEFAULTTONULL) == monitor) && | |
| 541 (GetWindowLong(taskbar, GWL_EXSTYLE) & WS_EX_TOPMOST); | |
| 542 } | |
| 543 | |
| 544 HANDLE GetSectionFromProcess(HANDLE section, HANDLE process, bool read_only) { | |
| 545 HANDLE valid_section = NULL; | |
| 546 DWORD access = STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ; | |
| 547 if (!read_only) | |
| 548 access |= FILE_MAP_WRITE; | |
| 549 DuplicateHandle(process, section, GetCurrentProcess(), &valid_section, access, | |
| 550 FALSE, 0); | |
| 551 return valid_section; | |
| 552 } | |
| 553 | |
| 554 bool DoesWindowBelongToActiveWindow(HWND window) { | |
| 555 DCHECK(window); | |
| 556 HWND top_window = ::GetAncestor(window, GA_ROOT); | |
| 557 if (!top_window) | |
| 558 return false; | |
| 559 | |
| 560 HWND active_top_window = ::GetAncestor(::GetForegroundWindow(), GA_ROOT); | |
| 561 return (top_window == active_top_window); | |
| 562 } | |
| 563 | |
| 564 void EnsureRectIsVisibleInRect(const gfx::Rect& parent_rect, | |
| 565 gfx::Rect* child_rect, | |
| 566 int padding) { | |
| 567 DCHECK(child_rect); | |
| 568 | |
| 569 // We use padding here because it allows some of the original web page to | |
| 570 // bleed through around the edges. | |
| 571 int twice_padding = padding * 2; | |
| 572 | |
| 573 // FIRST, clamp width and height so we don't open child windows larger than | |
| 574 // the containing parent. | |
| 575 if (child_rect->width() > (parent_rect.width() + twice_padding)) | |
| 576 child_rect->set_width(std::max(0, parent_rect.width() - twice_padding)); | |
| 577 if (child_rect->height() > parent_rect.height() + twice_padding) | |
| 578 child_rect->set_height(std::max(0, parent_rect.height() - twice_padding)); | |
| 579 | |
| 580 // SECOND, clamp x,y position to padding,padding so we don't position child | |
| 581 // windows in hyperspace. | |
| 582 // TODO(mpcomplete): I don't see what the second check in each 'if' does that | |
| 583 // isn't handled by the LAST set of 'ifs'. Maybe we can remove it. | |
| 584 if (child_rect->x() < parent_rect.x() || | |
| 585 child_rect->x() > parent_rect.right()) { | |
| 586 child_rect->set_x(parent_rect.x() + padding); | |
| 587 } | |
| 588 if (child_rect->y() < parent_rect.y() || | |
| 589 child_rect->y() > parent_rect.bottom()) { | |
| 590 child_rect->set_y(parent_rect.y() + padding); | |
| 591 } | |
| 592 | |
| 593 // LAST, nudge the window back up into the client area if its x,y position is | |
| 594 // within the parent bounds but its width/height place it off-screen. | |
| 595 if (child_rect->bottom() > parent_rect.bottom()) | |
| 596 child_rect->set_y(parent_rect.bottom() - child_rect->height() - padding); | |
| 597 if (child_rect->right() > parent_rect.right()) | |
| 598 child_rect->set_x(parent_rect.right() - child_rect->width() - padding); | |
| 599 } | |
| 600 | |
| 601 void SetChildBounds(HWND child_window, HWND parent_window, | |
| 602 HWND insert_after_window, const gfx::Rect& bounds, | |
| 603 int padding, unsigned long flags) { | |
| 604 DCHECK(IsWindow(child_window)); | |
| 605 | |
| 606 // First figure out the bounds of the parent. | |
| 607 RECT parent_rect = {0}; | |
| 608 if (parent_window) { | |
| 609 GetClientRect(parent_window, &parent_rect); | |
| 610 } else { | |
| 611 // If there is no parent, we consider the bounds of the monitor the window | |
| 612 // is on to be the parent bounds. | |
| 613 | |
| 614 // If the child_window isn't visible yet and we've been given a valid, | |
| 615 // visible insert after window, use that window to locate the correct | |
| 616 // monitor instead. | |
| 617 HWND window = child_window; | |
| 618 if (!IsWindowVisible(window) && IsWindow(insert_after_window) && | |
| 619 IsWindowVisible(insert_after_window)) | |
| 620 window = insert_after_window; | |
| 621 | |
| 622 POINT window_point = { bounds.x(), bounds.y() }; | |
| 623 HMONITOR monitor = MonitorFromPoint(window_point, | |
| 624 MONITOR_DEFAULTTONEAREST); | |
| 625 if (monitor) { | |
| 626 MONITORINFO mi = {0}; | |
| 627 mi.cbSize = sizeof(mi); | |
| 628 GetMonitorInfo(monitor, &mi); | |
| 629 parent_rect = mi.rcWork; | |
| 630 } else { | |
| 631 NOTREACHED() << "Unable to get default monitor"; | |
| 632 } | |
| 633 } | |
| 634 | |
| 635 gfx::Rect actual_bounds = bounds; | |
| 636 EnsureRectIsVisibleInRect(gfx::Rect(parent_rect), &actual_bounds, padding); | |
| 637 | |
| 638 SetWindowPos(child_window, insert_after_window, actual_bounds.x(), | |
| 639 actual_bounds.y(), actual_bounds.width(), | |
| 640 actual_bounds.height(), flags); | |
| 641 } | |
| 642 | |
| 643 gfx::Rect GetMonitorBoundsForRect(const gfx::Rect& rect) { | |
| 644 RECT p_rect = rect.ToRECT(); | |
| 645 HMONITOR monitor = MonitorFromRect(&p_rect, MONITOR_DEFAULTTONEAREST); | |
| 646 if (monitor) { | |
| 647 MONITORINFO mi = {0}; | |
| 648 mi.cbSize = sizeof(mi); | |
| 649 GetMonitorInfo(monitor, &mi); | |
| 650 return gfx::Rect(mi.rcWork); | |
| 651 } | |
| 652 NOTREACHED(); | |
| 653 return gfx::Rect(); | |
| 654 } | |
| 655 | |
| 656 bool IsNumPadDigit(int key_code, bool extended_key) { | |
| 657 if (key_code >= VK_NUMPAD0 && key_code <= VK_NUMPAD9) | |
| 658 return true; | |
| 659 | |
| 660 // Check for num pad keys without NumLock. | |
| 661 // Note: there is no easy way to know if a the key that was pressed comes from | |
| 662 // the num pad or the rest of the keyboard. Investigating how | |
| 663 // TranslateMessage() generates the WM_KEYCHAR from an | |
| 664 // ALT + <NumPad sequences> it appears it looks at the extended key flag | |
| 665 // (which is on if the key pressed comes from one of the 3 clusters to | |
| 666 // the left of the numeric keypad). So we use it as well. | |
| 667 return !extended_key && | |
| 668 ((key_code >= VK_PRIOR && key_code <= VK_DOWN) || // All keys but 5 | |
| 669 // and 0. | |
| 670 (key_code == VK_CLEAR) || // Key 5. | |
| 671 (key_code == VK_INSERT)); // Key 0. | |
| 672 } | |
| 673 | |
| 674 void GrabWindowSnapshot(HWND window_handle, | |
| 675 std::vector<unsigned char>* png_representation) { | |
| 676 // Create a memory DC that's compatible with the window. | |
| 677 CWindowDC window_hdc(window_handle); | |
| 678 CDC mem_hdc(::CreateCompatibleDC(window_hdc)); | |
| 679 | |
| 680 // Create a DIB that's the same size as the window. | |
| 681 RECT content_rect = {0, 0, 0, 0}; | |
| 682 ::GetWindowRect(window_handle, &content_rect); | |
| 683 content_rect.right++; // Match what PrintWindow wants. | |
| 684 int width = content_rect.right - content_rect.left; | |
| 685 int height = content_rect.bottom - content_rect.top; | |
| 686 BITMAPINFOHEADER hdr; | |
| 687 gfx::CreateBitmapHeader(width, height, &hdr); | |
| 688 unsigned char *bit_ptr = NULL; | |
| 689 CBitmap bitmap(::CreateDIBSection(mem_hdc, | |
| 690 reinterpret_cast<BITMAPINFO*>(&hdr), | |
| 691 DIB_RGB_COLORS, | |
| 692 reinterpret_cast<void **>(&bit_ptr), | |
| 693 NULL, 0)); | |
| 694 | |
| 695 mem_hdc.SelectBitmap(bitmap); | |
| 696 // Clear the bitmap to white (so that rounded corners on windows | |
| 697 // show up on a white background, and strangely-shaped windows | |
| 698 // look reasonable). Not capturing an alpha mask saves a | |
| 699 // bit of space. | |
| 700 mem_hdc.PatBlt(0, 0, width, height, WHITENESS); | |
| 701 // Grab a copy of the window | |
| 702 // First, see if PrintWindow is defined (it's not in Windows 2000). | |
| 703 typedef BOOL (WINAPI *PrintWindowPointer)(HWND, HDC, UINT); | |
| 704 PrintWindowPointer print_window = | |
| 705 reinterpret_cast<PrintWindowPointer>( | |
| 706 GetProcAddress(GetModuleHandle(L"User32.dll"), "PrintWindow")); | |
| 707 | |
| 708 // If PrintWindow is defined, use it. It will work on partially | |
| 709 // obscured windows, and works better for out of process sub-windows. | |
| 710 // Otherwise grab the bits we can get with BitBlt; it's better | |
| 711 // than nothing and will work fine in the average case (window is | |
| 712 // completely on screen). | |
| 713 if (print_window) | |
| 714 (*print_window)(window_handle, mem_hdc, 0); | |
| 715 else | |
| 716 mem_hdc.BitBlt(0, 0, width, height, window_hdc, 0, 0, SRCCOPY); | |
| 717 | |
| 718 // We now have a copy of the window contents in a DIB, so | |
| 719 // encode it into a useful format for posting to the bug report | |
| 720 // server. | |
| 721 PNGEncoder::Encode(bit_ptr, PNGEncoder::FORMAT_BGRA, | |
| 722 width, height, width * 4, true, | |
| 723 png_representation); | |
| 724 } | |
| 725 | |
| 726 bool IsWindowActive(HWND hwnd) { | |
| 727 WINDOWINFO info; | |
| 728 return ::GetWindowInfo(hwnd, &info) && | |
| 729 ((info.dwWindowStatus & WS_ACTIVECAPTION) != 0); | |
| 730 } | |
| 731 | |
| 732 bool IsReservedName(const std::wstring& filename) { | |
| 733 // This list is taken from the MSDN article "Naming a file" | |
| 734 // http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx | |
| 735 // I also added clock$ because GetSaveFileName seems to consider it as a | |
| 736 // reserved name too. | |
| 737 static const wchar_t* const known_devices[] = { | |
| 738 L"con", L"prn", L"aux", L"nul", L"com1", L"com2", L"com3", L"com4", L"com5", | |
| 739 L"com6", L"com7", L"com8", L"com9", L"lpt1", L"lpt2", L"lpt3", L"lpt4", | |
| 740 L"lpt5", L"lpt6", L"lpt7", L"lpt8", L"lpt9", L"clock$" | |
| 741 }; | |
| 742 std::wstring filename_lower = StringToLowerASCII(filename); | |
| 743 | |
| 744 for (int i = 0; i < arraysize(known_devices); ++i) { | |
| 745 // Exact match. | |
| 746 if (filename_lower == known_devices[i]) | |
| 747 return true; | |
| 748 // Starts with "DEVICE.". | |
| 749 if (filename_lower.find(std::wstring(known_devices[i]) + L".") == 0) | |
| 750 return true; | |
| 751 } | |
| 752 | |
| 753 static const wchar_t* const magic_names[] = { | |
| 754 // These file names are used by the "Customize folder" feature of the shell. | |
| 755 L"desktop.ini", | |
| 756 L"thumbs.db", | |
| 757 }; | |
| 758 | |
| 759 for (int i = 0; i < arraysize(magic_names); ++i) { | |
| 760 if (filename_lower == magic_names[i]) | |
| 761 return true; | |
| 762 } | |
| 763 | |
| 764 return false; | |
| 765 } | |
| 766 | |
| 767 bool IsShellIntegratedExtension(const std::wstring& extension) { | |
| 768 std::wstring extension_lower = StringToLowerASCII(extension); | |
| 769 | |
| 770 static const wchar_t* const integrated_extensions[] = { | |
| 771 // See <http://msdn.microsoft.com/en-us/library/ms811694.aspx>. | |
| 772 L"local", | |
| 773 // Right-clicking on shortcuts can be magical. | |
| 774 L"lnk", | |
| 775 }; | |
| 776 | |
| 777 for (int i = 0; i < arraysize(integrated_extensions); ++i) { | |
| 778 if (extension_lower == integrated_extensions[i]) | |
| 779 return true; | |
| 780 } | |
| 781 | |
| 782 // See <http://www.juniper.net/security/auto/vulnerabilities/vuln2612.html>. | |
| 783 // That vulnerability report is not exactly on point, but files become magical | |
| 784 // if their end in a CLSID. Here we block extensions that look like CLSIDs. | |
| 785 if (extension_lower.size() > 0 && extension_lower.at(0) == L'{' && | |
| 786 extension_lower.at(extension_lower.length() - 1) == L'}') | |
| 787 return true; | |
| 788 | |
| 789 return false; | |
| 790 } | |
| 791 | |
| 792 // In addition to passing the RTL flags to ::MessageBox if we are running in an | |
| 793 // RTL locale, we need to make sure that LTR strings are rendered correctly by | |
| 794 // adding the appropriate Unicode directionality marks. | |
| 795 int MessageBox(HWND hwnd, | |
| 796 const std::wstring& text, | |
| 797 const std::wstring& caption, | |
| 798 UINT flags) { | |
| 799 UINT actual_flags = flags; | |
| 800 if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) | |
| 801 actual_flags |= MB_RIGHT | MB_RTLREADING; | |
| 802 | |
| 803 std::wstring localized_text; | |
| 804 const wchar_t* text_ptr = text.c_str(); | |
| 805 if (l10n_util::AdjustStringForLocaleDirection(text, &localized_text)) | |
| 806 text_ptr = localized_text.c_str(); | |
| 807 | |
| 808 std::wstring localized_caption; | |
| 809 const wchar_t* caption_ptr = caption.c_str(); | |
| 810 if (l10n_util::AdjustStringForLocaleDirection(caption, &localized_caption)) | |
| 811 caption_ptr = localized_caption.c_str(); | |
| 812 | |
| 813 return ::MessageBox(hwnd, text_ptr, caption_ptr, actual_flags); | |
| 814 } | |
| 815 | |
| 816 ChromeFont GetWindowTitleFont() { | |
| 817 NONCLIENTMETRICS ncm; | |
| 818 win_util::GetNonClientMetrics(&ncm); | |
| 819 l10n_util::AdjustUIFont(&(ncm.lfCaptionFont)); | |
| 820 ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont))); | |
| 821 return ChromeFont::CreateFont(caption_font); | |
| 822 } | |
| 823 | |
| 824 } // namespace win_util | |
| OLD | NEW |