dock_info_win.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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/browser/ui/tabs/dock_info.h" 6 7#include "chrome/browser/ui/browser.h" 8#include "chrome/browser/ui/browser_list.h" 9#include "chrome/browser/ui/browser_window.h" 10#include "chrome/browser/views/frame/browser_view.h" 11#include "chrome/browser/views/tabs/tab.h" 12 13namespace { 14 15// BaseWindowFinder ----------------------------------------------------------- 16 17// Base class used to locate a window. This is intended to be used with the 18// various win32 functions that iterate over windows. 19// 20// A subclass need only override ShouldStopIterating to determine when 21// iteration should stop. 22class BaseWindowFinder { 23 public: 24 // Creates a BaseWindowFinder with the specified set of HWNDs to ignore. 25 explicit BaseWindowFinder(const std::set<HWND>& ignore) : ignore_(ignore) {} 26 virtual ~BaseWindowFinder() {} 27 28 protected: 29 // Returns true if iteration should stop, false if iteration should continue. 30 virtual bool ShouldStopIterating(HWND window) = 0; 31 32 static BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) { 33 BaseWindowFinder* finder = reinterpret_cast<BaseWindowFinder*>(lParam); 34 if (finder->ignore_.find(hwnd) != finder->ignore_.end()) 35 return TRUE; 36 37 return finder->ShouldStopIterating(hwnd) ? FALSE : TRUE; 38 } 39 40 private: 41 const std::set<HWND>& ignore_; 42 43 DISALLOW_COPY_AND_ASSIGN(BaseWindowFinder); 44}; 45 46// TopMostFinder -------------------------------------------------------------- 47 48// Helper class to determine if a particular point of a window is not obscured 49// by another window. 50class TopMostFinder : public BaseWindowFinder { 51 public: 52 // Returns true if |window| is the topmost window at the location 53 // |screen_loc|, not including the windows in |ignore|. 54 static bool IsTopMostWindowAtPoint(HWND window, 55 const gfx::Point& screen_loc, 56 const std::set<HWND>& ignore) { 57 TopMostFinder finder(window, screen_loc, ignore); 58 return finder.is_top_most_; 59 } 60 61 virtual bool ShouldStopIterating(HWND hwnd) { 62 if (hwnd == target_) { 63 // Window is topmost, stop iterating. 64 is_top_most_ = true; 65 return true; 66 } 67 68 if (!IsWindowVisible(hwnd)) { 69 // The window isn't visible, keep iterating. 70 return false; 71 } 72 73 RECT r; 74 if (!GetWindowRect(hwnd, &r) || !PtInRect(&r, screen_loc_.ToPOINT())) { 75 // The window doesn't contain the point, keep iterating. 76 return false; 77 } 78 79 LONG ex_styles = GetWindowLong(hwnd, GWL_EXSTYLE); 80 if (ex_styles & WS_EX_TRANSPARENT || ex_styles & WS_EX_LAYERED) { 81 // Mouse events fall through WS_EX_TRANSPARENT windows, so we ignore them. 82 // 83 // WS_EX_LAYERED is trickier. Apps like Switcher create a totally 84 // transparent WS_EX_LAYERED window that is always on top. If we don't 85 // ignore WS_EX_LAYERED windows and there are totally transparent 86 // WS_EX_LAYERED windows then there are effectively holes on the screen 87 // that the user can't reattach tabs to. So we ignore them. This is a bit 88 // problematic in so far as WS_EX_LAYERED windows need not be totally 89 // transparent in which case we treat chrome windows as not being obscured 90 // when they really are, but this is better than not being able to 91 // reattach tabs. 92 return false; 93 } 94 95 // hwnd is at the point. Make sure the point is within the windows region. 96 if (GetWindowRgn(hwnd, tmp_region_.Get()) == ERROR) { 97 // There's no region on the window and the window contains the point. Stop 98 // iterating. 99 return true; 100 } 101 102 // The region is relative to the window's rect. 103 BOOL is_point_in_region = PtInRegion(tmp_region_.Get(), 104 screen_loc_.x() - r.left, screen_loc_.y() - r.top); 105 tmp_region_ = CreateRectRgn(0, 0, 0, 0); 106 // Stop iterating if the region contains the point. 107 return !!is_point_in_region; 108 } 109 110 private: 111 TopMostFinder(HWND window, 112 const gfx::Point& screen_loc, 113 const std::set<HWND>& ignore) 114 : BaseWindowFinder(ignore), 115 target_(window), 116 screen_loc_(screen_loc), 117 is_top_most_(false), 118 tmp_region_(CreateRectRgn(0, 0, 0, 0)) { 119 EnumWindows(WindowCallbackProc, reinterpret_cast<LPARAM>(this)); 120 } 121 122 // The window we're looking for. 123 HWND target_; 124 125 // Location of window to find. 126 gfx::Point screen_loc_; 127 128 // Is target_ the top most window? This is initially false but set to true 129 // in ShouldStopIterating if target_ is passed in. 130 bool is_top_most_; 131 132 ScopedRegion tmp_region_; 133 134 DISALLOW_COPY_AND_ASSIGN(TopMostFinder); 135}; 136 137// WindowFinder --------------------------------------------------------------- 138 139// Helper class to determine if a particular point contains a window from our 140// process. 141class LocalProcessWindowFinder : public BaseWindowFinder { 142 public: 143 // Returns the hwnd from our process at screen_loc that is not obscured by 144 // another window. Returns NULL otherwise. 145 static HWND GetProcessWindowAtPoint(const gfx::Point& screen_loc, 146 const std::set<HWND>& ignore) { 147 LocalProcessWindowFinder finder(screen_loc, ignore); 148 if (finder.result_ && 149 TopMostFinder::IsTopMostWindowAtPoint(finder.result_, screen_loc, 150 ignore)) { 151 return finder.result_; 152 } 153 return NULL; 154 } 155 156 protected: 157 virtual bool ShouldStopIterating(HWND hwnd) { 158 RECT r; 159 if (IsWindowVisible(hwnd) && GetWindowRect(hwnd, &r) && 160 PtInRect(&r, screen_loc_.ToPOINT())) { 161 result_ = hwnd; 162 return true; 163 } 164 return false; 165 } 166 167 private: 168 LocalProcessWindowFinder(const gfx::Point& screen_loc, 169 const std::set<HWND>& ignore) 170 : BaseWindowFinder(ignore), 171 screen_loc_(screen_loc), 172 result_(NULL) { 173 EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 174 reinterpret_cast<LPARAM>(this)); 175 } 176 177 // Position of the mouse. 178 gfx::Point screen_loc_; 179 180 // The resulting window. This is initially null but set to true in 181 // ShouldStopIterating if an appropriate window is found. 182 HWND result_; 183 184 DISALLOW_COPY_AND_ASSIGN(LocalProcessWindowFinder); 185}; 186 187// DockToWindowFinder --------------------------------------------------------- 188 189// Helper class for creating a DockInfo from a specified point. 190class DockToWindowFinder : public BaseWindowFinder { 191 public: 192 // Returns the DockInfo for the specified point. If there is no docking 193 // position for the specified point the returned DockInfo has a type of NONE. 194 static DockInfo GetDockInfoAtPoint(const gfx::Point& screen_loc, 195 const std::set<HWND>& ignore) { 196 DockToWindowFinder finder(screen_loc, ignore); 197 if (!finder.result_.window() || 198 !TopMostFinder::IsTopMostWindowAtPoint(finder.result_.window(), 199 finder.result_.hot_spot(), 200 ignore)) { 201 finder.result_.set_type(DockInfo::NONE); 202 } 203 return finder.result_; 204 } 205 206 protected: 207 virtual bool ShouldStopIterating(HWND hwnd) { 208 BrowserView* window = BrowserView::GetBrowserViewForNativeWindow(hwnd); 209 RECT bounds; 210 if (!window || !IsWindowVisible(hwnd) || 211 !GetWindowRect(hwnd, &bounds)) { 212 return false; 213 } 214 215 // Check the three corners we allow docking to. We don't currently allow 216 // docking to top of window as it conflicts with docking to the tab strip. 217 if (CheckPoint(hwnd, bounds.left, (bounds.top + bounds.bottom) / 2, 218 DockInfo::LEFT_OF_WINDOW) || 219 CheckPoint(hwnd, bounds.right - 1, (bounds.top + bounds.bottom) / 2, 220 DockInfo::RIGHT_OF_WINDOW) || 221 CheckPoint(hwnd, (bounds.left + bounds.right) / 2, bounds.bottom - 1, 222 DockInfo::BOTTOM_OF_WINDOW)) { 223 return true; 224 } 225 return false; 226 } 227 228 private: 229 DockToWindowFinder(const gfx::Point& screen_loc, 230 const std::set<HWND>& ignore) 231 : BaseWindowFinder(ignore), 232 screen_loc_(screen_loc) { 233 HMONITOR monitor = MonitorFromPoint(screen_loc.ToPOINT(), 234 MONITOR_DEFAULTTONULL); 235 MONITORINFO monitor_info = {0}; 236 monitor_info.cbSize = sizeof(MONITORINFO); 237 if (monitor && GetMonitorInfo(monitor, &monitor_info)) { 238 result_.set_monitor_bounds(gfx::Rect(monitor_info.rcWork)); 239 EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 240 reinterpret_cast<LPARAM>(this)); 241 } 242 } 243 244 bool CheckPoint(HWND hwnd, int x, int y, DockInfo::Type type) { 245 bool in_enable_area; 246 if (DockInfo::IsCloseToPoint(screen_loc_, x, y, &in_enable_area)) { 247 result_.set_in_enable_area(in_enable_area); 248 result_.set_window(hwnd); 249 result_.set_type(type); 250 result_.set_hot_spot(gfx::Point(x, y)); 251 // Only show the hotspot if the monitor contains the bounds of the popup 252 // window. Otherwise we end with a weird situation where the popup window 253 // isn't completely visible. 254 return result_.monitor_bounds().Contains(result_.GetPopupRect()); 255 } 256 return false; 257 } 258 259 // The location to look for. 260 gfx::Point screen_loc_; 261 262 // The resulting DockInfo. 263 DockInfo result_; 264 265 DISALLOW_COPY_AND_ASSIGN(DockToWindowFinder); 266}; 267 268} // namespace 269 270// DockInfo ------------------------------------------------------------------- 271 272// static 273DockInfo DockInfo::GetDockInfoAtPoint(const gfx::Point& screen_point, 274 const std::set<HWND>& ignore) { 275 if (factory_) 276 return factory_->GetDockInfoAtPoint(screen_point, ignore); 277 278 // Try docking to a window first. 279 DockInfo info = DockToWindowFinder::GetDockInfoAtPoint(screen_point, ignore); 280 if (info.type() != DockInfo::NONE) 281 return info; 282 283 // No window relative positions. Try monitor relative positions. 284 const gfx::Rect& m_bounds = info.monitor_bounds(); 285 int mid_x = m_bounds.x() + m_bounds.width() / 2; 286 int mid_y = m_bounds.y() + m_bounds.height() / 2; 287 288 bool result = 289 info.CheckMonitorPoint(screen_point, mid_x, m_bounds.y(), 290 DockInfo::MAXIMIZE) || 291 info.CheckMonitorPoint(screen_point, mid_x, m_bounds.bottom(), 292 DockInfo::BOTTOM_HALF) || 293 info.CheckMonitorPoint(screen_point, m_bounds.x(), mid_y, 294 DockInfo::LEFT_HALF) || 295 info.CheckMonitorPoint(screen_point, m_bounds.right(), mid_y, 296 DockInfo::RIGHT_HALF); 297 298 return info; 299} 300 301HWND DockInfo::GetLocalProcessWindowAtPoint(const gfx::Point& screen_point, 302 const std::set<HWND>& ignore) { 303 if (factory_) 304 return factory_->GetLocalProcessWindowAtPoint(screen_point, ignore); 305 return 306 LocalProcessWindowFinder::GetProcessWindowAtPoint(screen_point, ignore); 307} 308 309bool DockInfo::GetWindowBounds(gfx::Rect* bounds) const { 310 RECT window_rect; 311 if (!window() || !GetWindowRect(window(), &window_rect)) 312 return false; 313 *bounds = gfx::Rect(window_rect); 314 return true; 315} 316 317void DockInfo::SizeOtherWindowTo(const gfx::Rect& bounds) const { 318 if (IsZoomed(window())) { 319 // We're docking relative to another window, we need to make sure the 320 // window we're docking to isn't maximized. 321 ShowWindow(window(), SW_RESTORE | SW_SHOWNA); 322 } 323 SetWindowPos(window(), HWND_TOP, bounds.x(), bounds.y(), bounds.width(), 324 bounds.height(), SWP_NOACTIVATE | SWP_NOOWNERZORDER); 325} 326