Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / ui / gfx / win / hwnd_util.cc
blob5700ccfd01148e73e9876351adcdb25a4edb8e3a
1 // Copyright (c) 2012 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.
5 #include "ui/gfx/win/hwnd_util.h"
7 #include "base/i18n/rtl.h"
8 #include "base/strings/string_util.h"
9 #include "base/tracked_objects.h"
10 #include "base/win/metro.h"
11 #include "base/win/win_util.h"
12 #include "ui/gfx/geometry/point.h"
13 #include "ui/gfx/geometry/rect.h"
14 #include "ui/gfx/geometry/size.h"
16 namespace gfx {
18 namespace {
20 // Adjust the window to fit.
21 void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) {
22 if (fit_to_monitor) {
23 // Get the monitor.
24 HMONITOR hmon = MonitorFromRect(&bounds, MONITOR_DEFAULTTONEAREST);
25 if (hmon) {
26 MONITORINFO mi;
27 mi.cbSize = sizeof(mi);
28 GetMonitorInfo(hmon, &mi);
29 Rect window_rect(bounds);
30 Rect monitor_rect(mi.rcWork);
31 Rect new_window_rect = window_rect;
32 new_window_rect.AdjustToFit(monitor_rect);
33 if (new_window_rect != window_rect) {
34 // Window doesn't fit on monitor, move and possibly resize.
35 SetWindowPos(hwnd, 0, new_window_rect.x(), new_window_rect.y(),
36 new_window_rect.width(), new_window_rect.height(),
37 SWP_NOACTIVATE | SWP_NOZORDER);
38 return;
40 // Else fall through.
41 } else {
42 NOTREACHED() << "Unable to find default monitor";
43 // Fall through.
45 } // Else fall through.
47 // The window is not being fit to monitor, or the window fits on the monitor
48 // as is, or we have no monitor info; reset the bounds.
49 ::SetWindowPos(hwnd, 0, bounds.left, bounds.top,
50 bounds.right - bounds.left, bounds.bottom - bounds.top,
51 SWP_NOACTIVATE | SWP_NOZORDER);
54 // Turn off optimizations for these functions so they show up in crash reports.
55 MSVC_DISABLE_OPTIMIZE();
57 void CrashOutOfMemory() {
58 PLOG(FATAL);
61 void CrashAccessDenied() {
62 PLOG(FATAL);
65 // Crash isn't one of the ones we commonly see.
66 void CrashOther() {
67 PLOG(FATAL);
70 MSVC_ENABLE_OPTIMIZE();
72 } // namespace
74 base::string16 GetClassName(HWND window) {
75 // GetClassNameW will return a truncated result (properly null terminated) if
76 // the given buffer is not large enough. So, it is not possible to determine
77 // that we got the entire class name if the result is exactly equal to the
78 // size of the buffer minus one.
79 DWORD buffer_size = MAX_PATH;
80 while (true) {
81 std::wstring output;
82 DWORD size_ret = GetClassNameW(
83 window, base::WriteInto(&output, buffer_size), buffer_size);
84 if (size_ret == 0)
85 break;
86 if (size_ret < (buffer_size - 1)) {
87 output.resize(size_ret);
88 return output;
90 buffer_size *= 2;
92 return std::wstring(); // error
95 #pragma warning(push)
96 #pragma warning(disable:4312 4244)
98 WNDPROC SetWindowProc(HWND hwnd, WNDPROC proc) {
99 // The reason we don't return the SetwindowLongPtr() value is that it returns
100 // the orignal window procedure and not the current one. I don't know if it is
101 // a bug or an intended feature.
102 WNDPROC oldwindow_proc =
103 reinterpret_cast<WNDPROC>(GetWindowLongPtr(hwnd, GWLP_WNDPROC));
104 SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(proc));
105 return oldwindow_proc;
108 void* SetWindowUserData(HWND hwnd, void* user_data) {
109 return
110 reinterpret_cast<void*>(SetWindowLongPtr(hwnd, GWLP_USERDATA,
111 reinterpret_cast<LONG_PTR>(user_data)));
114 void* GetWindowUserData(HWND hwnd) {
115 DWORD process_id = 0;
116 GetWindowThreadProcessId(hwnd, &process_id);
117 // A window outside the current process needs to be ignored.
118 if (process_id != ::GetCurrentProcessId())
119 return NULL;
120 return reinterpret_cast<void*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
123 #pragma warning(pop)
125 bool DoesWindowBelongToActiveWindow(HWND window) {
126 DCHECK(window);
127 HWND top_window = ::GetAncestor(window, GA_ROOT);
128 if (!top_window)
129 return false;
131 HWND active_top_window = ::GetAncestor(::GetForegroundWindow(), GA_ROOT);
132 return (top_window == active_top_window);
135 void CenterAndSizeWindow(HWND parent,
136 HWND window,
137 const Size& pref) {
138 DCHECK(window && pref.width() > 0 && pref.height() > 0);
140 // Calculate the ideal bounds.
141 RECT window_bounds;
142 RECT center_bounds = {0};
143 if (parent) {
144 // If there is a parent, center over the parents bounds.
145 ::GetWindowRect(parent, &center_bounds);
148 if (::IsRectEmpty(&center_bounds)) {
149 // No parent or no parent rect. Center over the monitor the window is on.
150 HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);
151 if (monitor) {
152 MONITORINFO mi = {0};
153 mi.cbSize = sizeof(mi);
154 GetMonitorInfo(monitor, &mi);
155 center_bounds = mi.rcWork;
156 } else {
157 NOTREACHED() << "Unable to get default monitor";
161 window_bounds.left = center_bounds.left;
162 if (pref.width() < (center_bounds.right - center_bounds.left)) {
163 window_bounds.left +=
164 (center_bounds.right - center_bounds.left - pref.width()) / 2;
166 window_bounds.right = window_bounds.left + pref.width();
168 window_bounds.top = center_bounds.top;
169 if (pref.height() < (center_bounds.bottom - center_bounds.top)) {
170 window_bounds.top +=
171 (center_bounds.bottom - center_bounds.top - pref.height()) / 2;
173 window_bounds.bottom = window_bounds.top + pref.height();
175 // If we're centering a child window, we are positioning in client
176 // coordinates, and as such we need to offset the target rectangle by the
177 // position of the parent window.
178 if (::GetWindowLong(window, GWL_STYLE) & WS_CHILD) {
179 DCHECK(parent && ::GetParent(window) == parent);
180 POINT topleft = { window_bounds.left, window_bounds.top };
181 ::MapWindowPoints(HWND_DESKTOP, parent, &topleft, 1);
182 window_bounds.left = topleft.x;
183 window_bounds.top = topleft.y;
184 window_bounds.right = window_bounds.left + pref.width();
185 window_bounds.bottom = window_bounds.top + pref.height();
188 AdjustWindowToFit(window, window_bounds, !parent);
191 void CheckWindowCreated(HWND hwnd) {
192 if (!hwnd) {
193 switch (GetLastError()) {
194 case ERROR_NOT_ENOUGH_MEMORY:
195 CrashOutOfMemory();
196 break;
197 case ERROR_ACCESS_DENIED:
198 CrashAccessDenied();
199 break;
200 default:
201 CrashOther();
202 break;
204 PLOG(FATAL);
208 void ShowSystemMenu(HWND window) {
209 RECT rect;
210 GetWindowRect(window, &rect);
211 Point point = Point(base::i18n::IsRTL() ? rect.right : rect.left, rect.top);
212 static const int kSystemMenuOffset = 10;
213 point.Offset(base::i18n::IsRTL() ? -kSystemMenuOffset : kSystemMenuOffset,
214 kSystemMenuOffset);
215 ShowSystemMenuAtPoint(window, point);
218 void ShowSystemMenuAtPoint(HWND window, const Point& point) {
219 // In the Metro process, we never want to show the system menu.
220 if (base::win::IsMetroProcess())
221 return;
222 UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD;
223 if (base::i18n::IsRTL())
224 flags |= TPM_RIGHTALIGN;
225 HMENU menu = GetSystemMenu(window, FALSE);
227 // Use task stopwatch to exclude the time while the context menu is open from
228 // the current task, if any.
229 tracked_objects::TaskStopwatch stopwatch;
230 stopwatch.Start();
231 const int command =
232 TrackPopupMenu(menu, flags, point.x(), point.y(), 0, window, NULL);
233 stopwatch.Stop();
235 if (command)
236 SendMessage(window, WM_SYSCOMMAND, command, 0);
239 extern "C" {
240 typedef HWND (*RootWindow)();
243 HWND GetWindowToParentTo(bool get_real_hwnd) {
244 HMODULE metro = base::win::GetMetroModule();
245 if (!metro)
246 return get_real_hwnd ? ::GetDesktopWindow() : HWND_DESKTOP;
247 // In windows 8 metro-mode the root window is not the desktop.
248 RootWindow root_window =
249 reinterpret_cast<RootWindow>(::GetProcAddress(metro, "GetRootWindow"));
250 return root_window();
253 } // namespace gfx