Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / tabs / dock_info_win.cc
blob5fa97e709f8ff88f7479966b642f9d3f1cf99b54
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 "chrome/browser/ui/tabs/dock_info.h"
7 #include "base/win/scoped_gdi_object.h"
8 #include "base/win/windows_version.h"
9 #include "chrome/browser/ui/ash/tabs/dock_info_ash.h"
10 #include "chrome/browser/ui/browser_list.h"
11 #include "chrome/browser/ui/browser_window.h"
12 #include "chrome/browser/ui/views/frame/browser_view.h"
13 #include "chrome/browser/ui/views/tabs/tab.h"
14 #include "ui/aura/root_window.h"
15 #include "ui/aura/window.h"
16 #include "ui/gfx/screen.h"
17 #include "ui/views/win/hwnd_util.h"
19 #if defined(USE_AURA)
20 #include "ui/views/widget/desktop_aura/desktop_root_window_host_win.h"
21 #endif
23 namespace {
25 // BaseWindowFinder -----------------------------------------------------------
27 // Base class used to locate a window. This is intended to be used with the
28 // various win32 functions that iterate over windows.
30 // A subclass need only override ShouldStopIterating to determine when
31 // iteration should stop.
32 class BaseWindowFinder {
33 public:
34 // Creates a BaseWindowFinder with the specified set of HWNDs to ignore.
35 explicit BaseWindowFinder(const std::set<HWND>& ignore) : ignore_(ignore) {}
36 virtual ~BaseWindowFinder() {}
38 protected:
39 static BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) {
40 // Cast must match that in as_lparam().
41 BaseWindowFinder* finder = reinterpret_cast<BaseWindowFinder*>(lParam);
42 if (finder->ignore_.find(hwnd) != finder->ignore_.end())
43 return TRUE;
45 return finder->ShouldStopIterating(hwnd) ? FALSE : TRUE;
48 LPARAM as_lparam() {
49 // Cast must match that in WindowCallbackProc().
50 return reinterpret_cast<LPARAM>(static_cast<BaseWindowFinder*>(this));
53 // Returns true if iteration should stop, false if iteration should continue.
54 virtual bool ShouldStopIterating(HWND window) = 0;
56 private:
57 const std::set<HWND>& ignore_;
59 DISALLOW_COPY_AND_ASSIGN(BaseWindowFinder);
62 // TopMostFinder --------------------------------------------------------------
64 // Helper class to determine if a particular point of a window is not obscured
65 // by another window.
66 class TopMostFinder : public BaseWindowFinder {
67 public:
68 // Returns true if |window| is the topmost window at the location
69 // |screen_loc|, not including the windows in |ignore|.
70 static bool IsTopMostWindowAtPoint(HWND window,
71 const gfx::Point& screen_loc,
72 const std::set<HWND>& ignore) {
73 TopMostFinder finder(window, screen_loc, ignore);
74 return finder.is_top_most_;
77 virtual bool ShouldStopIterating(HWND hwnd) {
78 if (hwnd == target_) {
79 // Window is topmost, stop iterating.
80 is_top_most_ = true;
81 return true;
84 if (!IsWindowVisible(hwnd)) {
85 // The window isn't visible, keep iterating.
86 return false;
89 RECT r;
90 if (!GetWindowRect(hwnd, &r) || !PtInRect(&r, screen_loc_.ToPOINT())) {
91 // The window doesn't contain the point, keep iterating.
92 return false;
95 LONG ex_styles = GetWindowLong(hwnd, GWL_EXSTYLE);
96 if (ex_styles & WS_EX_TRANSPARENT || ex_styles & WS_EX_LAYERED) {
97 // Mouse events fall through WS_EX_TRANSPARENT windows, so we ignore them.
99 // WS_EX_LAYERED is trickier. Apps like Switcher create a totally
100 // transparent WS_EX_LAYERED window that is always on top. If we don't
101 // ignore WS_EX_LAYERED windows and there are totally transparent
102 // WS_EX_LAYERED windows then there are effectively holes on the screen
103 // that the user can't reattach tabs to. So we ignore them. This is a bit
104 // problematic in so far as WS_EX_LAYERED windows need not be totally
105 // transparent in which case we treat chrome windows as not being obscured
106 // when they really are, but this is better than not being able to
107 // reattach tabs.
108 return false;
111 // hwnd is at the point. Make sure the point is within the windows region.
112 if (GetWindowRgn(hwnd, tmp_region_.Get()) == ERROR) {
113 // There's no region on the window and the window contains the point. Stop
114 // iterating.
115 return true;
118 // The region is relative to the window's rect.
119 BOOL is_point_in_region = PtInRegion(tmp_region_.Get(),
120 screen_loc_.x() - r.left, screen_loc_.y() - r.top);
121 tmp_region_ = CreateRectRgn(0, 0, 0, 0);
122 // Stop iterating if the region contains the point.
123 return !!is_point_in_region;
126 private:
127 TopMostFinder(HWND window,
128 const gfx::Point& screen_loc,
129 const std::set<HWND>& ignore)
130 : BaseWindowFinder(ignore),
131 target_(window),
132 screen_loc_(screen_loc),
133 is_top_most_(false),
134 tmp_region_(CreateRectRgn(0, 0, 0, 0)) {
135 EnumWindows(WindowCallbackProc, as_lparam());
138 // The window we're looking for.
139 HWND target_;
141 // Location of window to find.
142 gfx::Point screen_loc_;
144 // Is target_ the top most window? This is initially false but set to true
145 // in ShouldStopIterating if target_ is passed in.
146 bool is_top_most_;
148 base::win::ScopedRegion tmp_region_;
150 DISALLOW_COPY_AND_ASSIGN(TopMostFinder);
153 // WindowFinder ---------------------------------------------------------------
155 // Helper class to determine if a particular point contains a window from our
156 // process.
157 class LocalProcessWindowFinder : public BaseWindowFinder {
158 public:
159 // Returns the hwnd from our process at screen_loc that is not obscured by
160 // another window. Returns NULL otherwise.
161 static gfx::NativeWindow GetProcessWindowAtPoint(
162 const gfx::Point& screen_loc,
163 const std::set<HWND>& ignore) {
164 LocalProcessWindowFinder finder(screen_loc, ignore);
165 // Windows 8 has a window that appears first in the list of iterated
166 // windows, yet is not visually on top of everything.
167 // TODO(sky): figure out a better way to ignore this window.
168 if (finder.result_ &&
169 ((base::win::OSInfo::GetInstance()->version() >=
170 base::win::VERSION_WIN8) ||
171 TopMostFinder::IsTopMostWindowAtPoint(finder.result_, screen_loc,
172 ignore))) {
173 #if defined(USE_AURA)
174 return views::DesktopWindowTreeHostWin::GetContentWindowForHWND(
175 finder.result_);
176 #else
177 return finder.result_;
178 #endif
180 return NULL;
183 protected:
184 virtual bool ShouldStopIterating(HWND hwnd) {
185 RECT r;
186 if (IsWindowVisible(hwnd) && GetWindowRect(hwnd, &r) &&
187 PtInRect(&r, screen_loc_.ToPOINT())) {
188 result_ = hwnd;
189 return true;
191 return false;
194 private:
195 LocalProcessWindowFinder(const gfx::Point& screen_loc,
196 const std::set<HWND>& ignore)
197 : BaseWindowFinder(ignore),
198 screen_loc_(screen_loc),
199 result_(NULL) {
200 EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, as_lparam());
203 // Position of the mouse.
204 gfx::Point screen_loc_;
206 // The resulting window. This is initially null but set to true in
207 // ShouldStopIterating if an appropriate window is found.
208 HWND result_;
210 DISALLOW_COPY_AND_ASSIGN(LocalProcessWindowFinder);
213 // DockToWindowFinder ---------------------------------------------------------
215 // Helper class for creating a DockInfo from a specified point.
216 class DockToWindowFinder : public BaseWindowFinder {
217 public:
218 // Returns the DockInfo for the specified point. If there is no docking
219 // position for the specified point the returned DockInfo has a type of NONE.
220 static DockInfo GetDockInfoAtPoint(const gfx::Point& screen_loc,
221 const std::set<HWND>& ignore) {
222 DockToWindowFinder finder(screen_loc, ignore);
223 HWND hwnd = views::HWNDForNativeWindow(finder.result_.window());
224 if (!finder.result_.window() ||
225 !TopMostFinder::IsTopMostWindowAtPoint(hwnd,
226 finder.result_.hot_spot(),
227 ignore)) {
228 finder.result_.set_type(DockInfo::NONE);
230 return finder.result_;
233 protected:
234 virtual bool ShouldStopIterating(HWND hwnd) {
235 #if defined(USE_AURA)
236 BrowserView* window = BrowserView::GetBrowserViewForNativeWindow(
237 views::DesktopWindowTreeHostWin::GetContentWindowForHWND(hwnd));
238 #else
239 BrowserView* window = BrowserView::GetBrowserViewForNativeWindow(hwnd);
240 #endif
241 RECT bounds;
242 if (!window || !IsWindowVisible(hwnd) ||
243 !GetWindowRect(hwnd, &bounds)) {
244 return false;
247 // Check the three corners we allow docking to. We don't currently allow
248 // docking to top of window as it conflicts with docking to the tab strip.
249 if (CheckPoint(hwnd, bounds.left, (bounds.top + bounds.bottom) / 2,
250 DockInfo::LEFT_OF_WINDOW) ||
251 CheckPoint(hwnd, bounds.right - 1, (bounds.top + bounds.bottom) / 2,
252 DockInfo::RIGHT_OF_WINDOW) ||
253 CheckPoint(hwnd, (bounds.left + bounds.right) / 2, bounds.bottom - 1,
254 DockInfo::BOTTOM_OF_WINDOW)) {
255 return true;
257 return false;
260 private:
261 DockToWindowFinder(const gfx::Point& screen_loc,
262 const std::set<HWND>& ignore)
263 : BaseWindowFinder(ignore),
264 screen_loc_(screen_loc) {
265 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->
266 GetDisplayNearestPoint(screen_loc).bounds();
267 if (!work_area.IsEmpty()) {
268 result_.set_monitor_bounds(work_area);
269 EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, as_lparam());
273 bool CheckPoint(HWND hwnd, int x, int y, DockInfo::Type type) {
274 bool in_enable_area;
275 if (DockInfo::IsCloseToPoint(screen_loc_, x, y, &in_enable_area)) {
276 result_.set_in_enable_area(in_enable_area);
277 #if defined(USE_AURA)
278 result_.set_window(
279 views::DesktopWindowTreeHostWin::GetContentWindowForHWND(hwnd));
280 #else
281 result_.set_window(hwnd);
282 #endif
283 result_.set_type(type);
284 result_.set_hot_spot(gfx::Point(x, y));
285 // Only show the hotspot if the monitor contains the bounds of the popup
286 // window. Otherwise we end with a weird situation where the popup window
287 // isn't completely visible.
288 return result_.monitor_bounds().Contains(result_.GetPopupRect());
290 return false;
293 // The location to look for.
294 gfx::Point screen_loc_;
296 // The resulting DockInfo.
297 DockInfo result_;
299 DISALLOW_COPY_AND_ASSIGN(DockToWindowFinder);
302 std::set<HWND> RemapIgnoreSet(const std::set<gfx::NativeView>& ignore) {
303 #if defined(USE_AURA)
304 std::set<HWND> hwnd_set;
305 std::set<gfx::NativeView>::const_iterator it = ignore.begin();
306 for (; it != ignore.end(); ++it) {
307 HWND w = (*it)->GetDispatcher()->host()->GetAcceleratedWidget();
308 if (w)
309 hwnd_set.insert(w);
311 return hwnd_set;
312 #else
313 // NativeViews are already HWNDs on non-Aura Windows.
314 return ignore;
315 #endif
318 } // namespace
320 // DockInfo -------------------------------------------------------------------
322 // static
323 DockInfo DockInfo::GetDockInfoAtPoint(chrome::HostDesktopType host_desktop_type,
324 const gfx::Point& screen_point,
325 const std::set<gfx::NativeView>& ignore) {
326 #if defined(USE_AURA)
327 if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
328 return chrome::ash::GetDockInfoAtPointAsh(screen_point, ignore);
329 #endif
330 // Try docking to a window first.
331 DockInfo info = DockToWindowFinder::GetDockInfoAtPoint(
332 screen_point, RemapIgnoreSet(ignore));
333 if (info.type() != DockInfo::NONE)
334 return info;
336 // No window relative positions. Try monitor relative positions.
337 const gfx::Rect& m_bounds = info.monitor_bounds();
338 int mid_x = m_bounds.x() + m_bounds.width() / 2;
339 int mid_y = m_bounds.y() + m_bounds.height() / 2;
341 bool result =
342 info.CheckMonitorPoint(screen_point, mid_x, m_bounds.y(),
343 DockInfo::MAXIMIZE) ||
344 info.CheckMonitorPoint(screen_point, mid_x, m_bounds.bottom(),
345 DockInfo::BOTTOM_HALF) ||
346 info.CheckMonitorPoint(screen_point, m_bounds.x(), mid_y,
347 DockInfo::LEFT_HALF) ||
348 info.CheckMonitorPoint(screen_point, m_bounds.right(), mid_y,
349 DockInfo::RIGHT_HALF);
351 return info;
354 gfx::NativeView DockInfo::GetLocalProcessWindowAtPoint(
355 chrome::HostDesktopType host_desktop_type,
356 const gfx::Point& screen_point,
357 const std::set<gfx::NativeView>& ignore) {
358 #if defined(USE_AURA)
359 if (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
360 return chrome::ash::GetLocalProcessWindowAtPointAsh(screen_point, ignore);
361 #endif
362 return
363 LocalProcessWindowFinder::GetProcessWindowAtPoint(
364 screen_point, RemapIgnoreSet(ignore));
367 #if defined(USE_AURA)
369 bool DockInfo::GetWindowBounds(gfx::Rect* bounds) const {
370 if (!window())
371 return false;
372 *bounds = window_->bounds();
373 return true;
376 void DockInfo::SizeOtherWindowTo(const gfx::Rect& bounds) const {
377 window_->SetBounds(bounds);
380 #else // USE_AURA
382 bool DockInfo::GetWindowBounds(gfx::Rect* bounds) const {
383 RECT window_rect;
384 if (!window() || !GetWindowRect(window(), &window_rect))
385 return false;
386 *bounds = gfx::Rect(window_rect);
387 return true;
390 void DockInfo::SizeOtherWindowTo(const gfx::Rect& bounds) const {
391 if (IsZoomed(window())) {
392 // We're docking relative to another window, we need to make sure the
393 // window we're docking to isn't maximized.
394 ShowWindow(window(), SW_RESTORE | SW_SHOWNA);
396 SetWindowPos(window(), HWND_TOP, bounds.x(), bounds.y(), bounds.width(),
397 bounds.height(), SWP_NOACTIVATE | SWP_NOOWNERZORDER);
400 // static
401 int DockInfo::GetHotSpotDeltaY() {
402 return Tab::GetMinimumUnselectedSize().height() - 1;
405 #endif // !USE_AURA