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