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 |