• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 "base/win/scoped_gdi_object.h"
8 #include "chrome/browser/ui/browser_list.h"
9 #include "chrome/browser/ui/browser_window.h"
10 #include "chrome/browser/ui/views/frame/browser_view.h"
11 #include "chrome/browser/ui/views/tabs/tab.h"
12 #include "views/screen.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.
BaseWindowFinder(const std::set<HWND> & ignore)26   explicit BaseWindowFinder(const std::set<HWND>& ignore) : ignore_(ignore) {}
~BaseWindowFinder()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 
WindowCallbackProc(HWND hwnd,LPARAM lParam)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|.
IsTopMostWindowAtPoint(HWND window,const gfx::Point & screen_loc,const std::set<HWND> & 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 
ShouldStopIterating(HWND hwnd)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:
TopMostFinder(HWND window,const gfx::Point & screen_loc,const std::set<HWND> & ignore)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   base::win::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.
GetProcessWindowAtPoint(const gfx::Point & screen_loc,const std::set<HWND> & ignore)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:
ShouldStopIterating(HWND hwnd)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:
LocalProcessWindowFinder(const gfx::Point & screen_loc,const std::set<HWND> & ignore)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.
GetDockInfoAtPoint(const gfx::Point & screen_loc,const std::set<HWND> & ignore)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:
ShouldStopIterating(HWND hwnd)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:
DockToWindowFinder(const gfx::Point & screen_loc,const std::set<HWND> & ignore)230   DockToWindowFinder(const gfx::Point& screen_loc,
231                      const std::set<HWND>& ignore)
232       : BaseWindowFinder(ignore),
233         screen_loc_(screen_loc) {
234     gfx::Rect work_area = views::Screen::GetMonitorWorkAreaNearestPoint(
235         screen_loc);
236     if (!work_area.IsEmpty()) {
237       result_.set_monitor_bounds(work_area);
238       EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc,
239                         reinterpret_cast<LPARAM>(this));
240     }
241   }
242 
CheckPoint(HWND hwnd,int x,int y,DockInfo::Type type)243   bool CheckPoint(HWND hwnd, int x, int y, DockInfo::Type type) {
244     bool in_enable_area;
245     if (DockInfo::IsCloseToPoint(screen_loc_, x, y, &in_enable_area)) {
246       result_.set_in_enable_area(in_enable_area);
247       result_.set_window(hwnd);
248       result_.set_type(type);
249       result_.set_hot_spot(gfx::Point(x, y));
250       // Only show the hotspot if the monitor contains the bounds of the popup
251       // window. Otherwise we end with a weird situation where the popup window
252       // isn't completely visible.
253       return result_.monitor_bounds().Contains(result_.GetPopupRect());
254     }
255     return false;
256   }
257 
258   // The location to look for.
259   gfx::Point screen_loc_;
260 
261   // The resulting DockInfo.
262   DockInfo result_;
263 
264   DISALLOW_COPY_AND_ASSIGN(DockToWindowFinder);
265 };
266 
267 }  // namespace
268 
269 // DockInfo -------------------------------------------------------------------
270 
271 // static
GetDockInfoAtPoint(const gfx::Point & screen_point,const std::set<HWND> & ignore)272 DockInfo DockInfo::GetDockInfoAtPoint(const gfx::Point& screen_point,
273                                       const std::set<HWND>& ignore) {
274   if (factory_)
275     return factory_->GetDockInfoAtPoint(screen_point, ignore);
276 
277   // Try docking to a window first.
278   DockInfo info = DockToWindowFinder::GetDockInfoAtPoint(screen_point, ignore);
279   if (info.type() != DockInfo::NONE)
280     return info;
281 
282   // No window relative positions. Try monitor relative positions.
283   const gfx::Rect& m_bounds = info.monitor_bounds();
284   int mid_x = m_bounds.x() + m_bounds.width() / 2;
285   int mid_y = m_bounds.y() + m_bounds.height() / 2;
286 
287   bool result =
288       info.CheckMonitorPoint(screen_point, mid_x, m_bounds.y(),
289                              DockInfo::MAXIMIZE) ||
290       info.CheckMonitorPoint(screen_point, mid_x, m_bounds.bottom(),
291                              DockInfo::BOTTOM_HALF) ||
292       info.CheckMonitorPoint(screen_point, m_bounds.x(), mid_y,
293                              DockInfo::LEFT_HALF) ||
294       info.CheckMonitorPoint(screen_point, m_bounds.right(), mid_y,
295                              DockInfo::RIGHT_HALF);
296 
297   return info;
298 }
299 
GetLocalProcessWindowAtPoint(const gfx::Point & screen_point,const std::set<HWND> & ignore)300 HWND DockInfo::GetLocalProcessWindowAtPoint(const gfx::Point& screen_point,
301                                             const std::set<HWND>& ignore) {
302   if (factory_)
303     return factory_->GetLocalProcessWindowAtPoint(screen_point, ignore);
304   return
305       LocalProcessWindowFinder::GetProcessWindowAtPoint(screen_point, ignore);
306 }
307 
GetWindowBounds(gfx::Rect * bounds) const308 bool DockInfo::GetWindowBounds(gfx::Rect* bounds) const {
309   RECT window_rect;
310   if (!window() || !GetWindowRect(window(), &window_rect))
311     return false;
312   *bounds = gfx::Rect(window_rect);
313   return true;
314 }
315 
SizeOtherWindowTo(const gfx::Rect & bounds) const316 void DockInfo::SizeOtherWindowTo(const gfx::Rect& bounds) const {
317   if (IsZoomed(window())) {
318     // We're docking relative to another window, we need to make sure the
319     // window we're docking to isn't maximized.
320     ShowWindow(window(), SW_RESTORE | SW_SHOWNA);
321   }
322   SetWindowPos(window(), HWND_TOP, bounds.x(), bounds.y(), bounds.width(),
323                bounds.height(), SWP_NOACTIVATE | SWP_NOOWNERZORDER);
324 }
325