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. 4 5 #include "ui/base/win/hwnd_util.h" 6 7 #include "base/i18n/rtl.h" 8 #include "base/strings/string_util.h" 9 #include "base/win/metro.h" 10 #include "ui/gfx/point.h" 11 #include "ui/gfx/rect.h" 12 #include "ui/gfx/size.h" 13 14 namespace ui { 15 16 namespace { 17 18 // Adjust the window to fit. 19 void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) { 20 if (fit_to_monitor) { 21 // Get the monitor. 22 HMONITOR hmon = MonitorFromRect(&bounds, MONITOR_DEFAULTTONEAREST); 23 if (hmon) { 24 MONITORINFO mi; 25 mi.cbSize = sizeof(mi); 26 GetMonitorInfo(hmon, &mi); 27 gfx::Rect window_rect(bounds); 28 gfx::Rect monitor_rect(mi.rcWork); 29 gfx::Rect new_window_rect = window_rect; 30 new_window_rect.AdjustToFit(monitor_rect); 31 if (new_window_rect != window_rect) { 32 // Window doesn't fit on monitor, move and possibly resize. 33 SetWindowPos(hwnd, 0, new_window_rect.x(), new_window_rect.y(), 34 new_window_rect.width(), new_window_rect.height(), 35 SWP_NOACTIVATE | SWP_NOZORDER); 36 return; 37 } 38 // Else fall through. 39 } else { 40 NOTREACHED() << "Unable to find default monitor"; 41 // Fall through. 42 } 43 } // Else fall through. 44 45 // The window is not being fit to monitor, or the window fits on the monitor 46 // as is, or we have no monitor info; reset the bounds. 47 ::SetWindowPos(hwnd, 0, bounds.left, bounds.top, 48 bounds.right - bounds.left, bounds.bottom - bounds.top, 49 SWP_NOACTIVATE | SWP_NOZORDER); 50 } 51 52 } // namespace 53 54 string16 GetClassName(HWND window) { 55 // GetClassNameW will return a truncated result (properly null terminated) if 56 // the given buffer is not large enough. So, it is not possible to determine 57 // that we got the entire class name if the result is exactly equal to the 58 // size of the buffer minus one. 59 DWORD buffer_size = MAX_PATH; 60 while (true) { 61 std::wstring output; 62 DWORD size_ret = 63 GetClassNameW(window, WriteInto(&output, buffer_size), buffer_size); 64 if (size_ret == 0) 65 break; 66 if (size_ret < (buffer_size - 1)) { 67 output.resize(size_ret); 68 return output; 69 } 70 buffer_size *= 2; 71 } 72 return std::wstring(); // error 73 } 74 75 #pragma warning(push) 76 #pragma warning(disable:4312 4244) 77 78 WNDPROC SetWindowProc(HWND hwnd, WNDPROC proc) { 79 // The reason we don't return the SetwindowLongPtr() value is that it returns 80 // the orignal window procedure and not the current one. I don't know if it is 81 // a bug or an intended feature. 82 WNDPROC oldwindow_proc = 83 reinterpret_cast<WNDPROC>(GetWindowLongPtr(hwnd, GWLP_WNDPROC)); 84 SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(proc)); 85 return oldwindow_proc; 86 } 87 88 void* SetWindowUserData(HWND hwnd, void* user_data) { 89 return 90 reinterpret_cast<void*>(SetWindowLongPtr(hwnd, GWLP_USERDATA, 91 reinterpret_cast<LONG_PTR>(user_data))); 92 } 93 94 void* GetWindowUserData(HWND hwnd) { 95 DWORD process_id = 0; 96 DWORD thread_id = GetWindowThreadProcessId(hwnd, &process_id); 97 // A window outside the current process needs to be ignored. 98 if (process_id != ::GetCurrentProcessId()) 99 return NULL; 100 return reinterpret_cast<void*>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); 101 } 102 103 #pragma warning(pop) 104 105 bool DoesWindowBelongToActiveWindow(HWND window) { 106 DCHECK(window); 107 HWND top_window = ::GetAncestor(window, GA_ROOT); 108 if (!top_window) 109 return false; 110 111 HWND active_top_window = ::GetAncestor(::GetForegroundWindow(), GA_ROOT); 112 return (top_window == active_top_window); 113 } 114 115 void CenterAndSizeWindow(HWND parent, 116 HWND window, 117 const gfx::Size& pref) { 118 DCHECK(window && pref.width() > 0 && pref.height() > 0); 119 120 // Calculate the ideal bounds. 121 RECT window_bounds; 122 RECT center_bounds = {0}; 123 if (parent) { 124 // If there is a parent, center over the parents bounds. 125 ::GetWindowRect(parent, ¢er_bounds); 126 } 127 128 if (::IsRectEmpty(¢er_bounds)) { 129 // No parent or no parent rect. Center over the monitor the window is on. 130 HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST); 131 if (monitor) { 132 MONITORINFO mi = {0}; 133 mi.cbSize = sizeof(mi); 134 GetMonitorInfo(monitor, &mi); 135 center_bounds = mi.rcWork; 136 } else { 137 NOTREACHED() << "Unable to get default monitor"; 138 } 139 } 140 141 window_bounds.left = center_bounds.left; 142 if (pref.width() < (center_bounds.right - center_bounds.left)) { 143 window_bounds.left += 144 (center_bounds.right - center_bounds.left - pref.width()) / 2; 145 } 146 window_bounds.right = window_bounds.left + pref.width(); 147 148 window_bounds.top = center_bounds.top; 149 if (pref.height() < (center_bounds.bottom - center_bounds.top)) { 150 window_bounds.top += 151 (center_bounds.bottom - center_bounds.top - pref.height()) / 2; 152 } 153 window_bounds.bottom = window_bounds.top + pref.height(); 154 155 // If we're centering a child window, we are positioning in client 156 // coordinates, and as such we need to offset the target rectangle by the 157 // position of the parent window. 158 if (::GetWindowLong(window, GWL_STYLE) & WS_CHILD) { 159 DCHECK(parent && ::GetParent(window) == parent); 160 POINT topleft = { window_bounds.left, window_bounds.top }; 161 ::MapWindowPoints(HWND_DESKTOP, parent, &topleft, 1); 162 window_bounds.left = topleft.x; 163 window_bounds.top = topleft.y; 164 window_bounds.right = window_bounds.left + pref.width(); 165 window_bounds.bottom = window_bounds.top + pref.height(); 166 } 167 168 AdjustWindowToFit(window, window_bounds, !parent); 169 } 170 171 void CheckWindowCreated(HWND hwnd) { 172 if (!hwnd) 173 LOG_GETLASTERROR(FATAL); 174 } 175 176 void ShowSystemMenu(HWND window) { 177 RECT rect; 178 GetWindowRect(window, &rect); 179 gfx::Point point = gfx::Point(rect.left, rect.top); 180 static const int kSystemMenuOffset = 10; 181 point.Offset(kSystemMenuOffset, kSystemMenuOffset); 182 ShowSystemMenuAtPoint(window, point); 183 } 184 185 void ShowSystemMenuAtPoint(HWND window, const gfx::Point& point) { 186 // In the Metro process, we never want to show the system menu. 187 if (base::win::IsMetroProcess()) 188 return; 189 UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD; 190 if (base::i18n::IsRTL()) 191 flags |= TPM_RIGHTALIGN; 192 HMENU menu = GetSystemMenu(window, FALSE); 193 const int command = 194 TrackPopupMenu(menu, flags, point.x(), point.y(), 0, window, NULL); 195 if (command) 196 SendMessage(window, WM_SYSCOMMAND, command, 0); 197 } 198 199 extern "C" { 200 typedef HWND (*RootWindow)(); 201 } 202 203 HWND GetWindowToParentTo(bool get_real_hwnd) { 204 HMODULE metro = base::win::GetMetroModule(); 205 if (!metro) 206 return get_real_hwnd ? ::GetDesktopWindow() : HWND_DESKTOP; 207 // In windows 8 metro-mode the root window is not the desktop. 208 RootWindow root_window = 209 reinterpret_cast<RootWindow>(::GetProcAddress(metro, "GetRootWindow")); 210 return root_window(); 211 } 212 213 } // namespace ui 214