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 |