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 <gtk/gtk.h>
8
9 #include "base/logging.h"
10 #include "base/task.h"
11 #include "chrome/browser/ui/browser_list.h"
12 #include "chrome/browser/ui/browser_window.h"
13 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
14 #include "chrome/browser/ui/gtk/gtk_util.h"
15 #include "ui/base/x/x11_util.h"
16 #include "ui/gfx/native_widget_types.h"
17
18 ////////////////////////////////////////////////////////////////////////////////
19 // BaseWindowFinder
20 //
21 // Base class used to locate a window. A subclass need only override
22 // ShouldStopIterating to determine when iteration should stop.
23 class BaseWindowFinder : public ui::EnumerateWindowsDelegate {
24 public:
BaseWindowFinder(const std::set<GtkWidget * > & ignore)25 explicit BaseWindowFinder(const std::set<GtkWidget*>& ignore) {
26 std::set<GtkWidget*>::iterator iter;
27 for (iter = ignore.begin(); iter != ignore.end(); iter++) {
28 XID xid = ui::GetX11WindowFromGtkWidget(*iter);
29 ignore_.insert(xid);
30 }
31 }
32
~BaseWindowFinder()33 virtual ~BaseWindowFinder() {}
34
35 protected:
36 // Returns true if |window| is in the ignore list.
ShouldIgnoreWindow(XID window)37 bool ShouldIgnoreWindow(XID window) {
38 return (ignore_.find(window) != ignore_.end());
39 }
40
41 // Returns true if iteration should stop, false otherwise.
ShouldStopIterating(XID window)42 virtual bool ShouldStopIterating(XID window) {
43 return false;
44 }
45
46 private:
47 std::set<XID> ignore_;
48
49 DISALLOW_COPY_AND_ASSIGN(BaseWindowFinder);
50 };
51
52 ////////////////////////////////////////////////////////////////////////////////
53 // TopMostFinder
54 //
55 // Helper class to determine if a particular point of a window is not obscured
56 // by another window.
57 class TopMostFinder : public BaseWindowFinder {
58 public:
59 // Returns true if |window| is not obscured by another window at the
60 // location |screen_loc|, not including the windows in |ignore|.
IsTopMostWindowAtPoint(XID window,const gfx::Point & screen_loc,const std::set<GtkWidget * > & ignore)61 static bool IsTopMostWindowAtPoint(XID window,
62 const gfx::Point& screen_loc,
63 const std::set<GtkWidget*>& ignore) {
64 TopMostFinder finder(window, screen_loc, ignore);
65 return finder.is_top_most_;
66 }
67
68 protected:
ShouldStopIterating(XID window)69 virtual bool ShouldStopIterating(XID window) {
70 if (BaseWindowFinder::ShouldIgnoreWindow(window))
71 return false;
72
73 if (window == target_) {
74 // Window is topmost, stop iterating.
75 is_top_most_ = true;
76 return true;
77 }
78
79 if (!ui::IsWindowVisible(window)) {
80 // The window isn't visible, keep iterating.
81 return false;
82 }
83
84 gfx::Rect rect;
85 if (ui::GetWindowRect(window, &rect) && rect.Contains(screen_loc_)) {
86 // At this point we haven't found our target window, so this window is
87 // higher in the z-order than the target window. If this window contains
88 // the point, then we can stop the search now because this window is
89 // obscuring the target window at this point.
90 return true;
91 }
92
93 return false;
94 }
95
96 private:
TopMostFinder(XID window,const gfx::Point & screen_loc,const std::set<GtkWidget * > & ignore)97 TopMostFinder(XID window,
98 const gfx::Point& screen_loc,
99 const std::set<GtkWidget*>& ignore)
100 : BaseWindowFinder(ignore),
101 target_(window),
102 screen_loc_(screen_loc),
103 is_top_most_(false) {
104 gtk_util::EnumerateTopLevelWindows(this);
105 }
106
107 // The window we're looking for.
108 XID target_;
109
110 // Location of window to find.
111 gfx::Point screen_loc_;
112
113 // Is target_ the top most window? This is initially false but set to true
114 // in ShouldStopIterating if target_ is passed in.
115 bool is_top_most_;
116
117 DISALLOW_COPY_AND_ASSIGN(TopMostFinder);
118 };
119
120 ////////////////////////////////////////////////////////////////////////////////
121 // LocalProcessWindowFinder
122 //
123 // Helper class to determine if a particular point of a window from our process
124 // is not obscured by another window.
125 class LocalProcessWindowFinder : public BaseWindowFinder {
126 public:
127 // Returns the XID from our process at screen_loc that is not obscured by
128 // another window. Returns 0 otherwise.
GetProcessWindowAtPoint(const gfx::Point & screen_loc,const std::set<GtkWidget * > & ignore)129 static XID GetProcessWindowAtPoint(const gfx::Point& screen_loc,
130 const std::set<GtkWidget*>& ignore) {
131 LocalProcessWindowFinder finder(screen_loc, ignore);
132 if (finder.result_ &&
133 TopMostFinder::IsTopMostWindowAtPoint(finder.result_, screen_loc,
134 ignore)) {
135 return finder.result_;
136 }
137 return 0;
138 }
139
140 protected:
ShouldStopIterating(XID window)141 virtual bool ShouldStopIterating(XID window) {
142 if (BaseWindowFinder::ShouldIgnoreWindow(window))
143 return false;
144
145 // Check if this window is in our process.
146 if (!BrowserWindowGtk::GetBrowserWindowForXID(window))
147 return false;
148
149 if (!ui::IsWindowVisible(window))
150 return false;
151
152 gfx::Rect rect;
153 if (ui::GetWindowRect(window, &rect) && rect.Contains(screen_loc_)) {
154 result_ = window;
155 return true;
156 }
157
158 return false;
159 }
160
161 private:
LocalProcessWindowFinder(const gfx::Point & screen_loc,const std::set<GtkWidget * > & ignore)162 LocalProcessWindowFinder(const gfx::Point& screen_loc,
163 const std::set<GtkWidget*>& ignore)
164 : BaseWindowFinder(ignore),
165 screen_loc_(screen_loc),
166 result_(0) {
167 gtk_util::EnumerateTopLevelWindows(this);
168 }
169
170 // Position of the mouse.
171 gfx::Point screen_loc_;
172
173 // The resulting window. This is initially null but set to true in
174 // ShouldStopIterating if an appropriate window is found.
175 XID result_;
176
177 DISALLOW_COPY_AND_ASSIGN(LocalProcessWindowFinder);
178 };
179
180 // static
GetDockInfoAtPoint(const gfx::Point & screen_point,const std::set<GtkWidget * > & ignore)181 DockInfo DockInfo::GetDockInfoAtPoint(const gfx::Point& screen_point,
182 const std::set<GtkWidget*>& ignore) {
183 if (factory_)
184 return factory_->GetDockInfoAtPoint(screen_point, ignore);
185
186 NOTIMPLEMENTED();
187 return DockInfo();
188 }
189
190 // static
GetLocalProcessWindowAtPoint(const gfx::Point & screen_point,const std::set<GtkWidget * > & ignore)191 GtkWindow* DockInfo::GetLocalProcessWindowAtPoint(
192 const gfx::Point& screen_point,
193 const std::set<GtkWidget*>& ignore) {
194 if (factory_)
195 return factory_->GetLocalProcessWindowAtPoint(screen_point, ignore);
196
197 #if defined(OS_CHROMEOS) || defined(TOOLKIT_VIEWS)
198 return NULL;
199 #else
200 XID xid =
201 LocalProcessWindowFinder::GetProcessWindowAtPoint(screen_point, ignore);
202 return BrowserWindowGtk::GetBrowserWindowForXID(xid);
203 #endif
204 }
205
GetWindowBounds(gfx::Rect * bounds) const206 bool DockInfo::GetWindowBounds(gfx::Rect* bounds) const {
207 if (!window())
208 return false;
209
210 int x, y, w, h;
211 gtk_window_get_position(window(), &x, &y);
212 gtk_window_get_size(window(), &w, &h);
213 bounds->SetRect(x, y, w, h);
214 return true;
215 }
216
SizeOtherWindowTo(const gfx::Rect & bounds) const217 void DockInfo::SizeOtherWindowTo(const gfx::Rect& bounds) const {
218 gtk_window_move(window(), bounds.x(), bounds.y());
219 gtk_window_resize(window(), bounds.width(), bounds.height());
220 }
221