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/window_sizer.h" | |
6 | |
7 #include "chrome/browser/browser_list.h" | |
8 #include "chrome/browser/browser_process.h" | |
9 #include "chrome/browser/browser_window.h" | |
10 #include "chrome/browser/prefs/pref_service.h" | |
11 #include "chrome/browser/ui/browser.h" | |
12 #include "chrome/common/pref_names.h" | |
13 | |
14 /////////////////////////////////////////////////////////////////////////////// | |
15 // An implementation of WindowSizer::StateProvider that gets the last active | |
16 // and persistent state from the browser window and the user's profile. | |
17 class DefaultStateProvider : public WindowSizer::StateProvider { | |
18 public: | |
19 explicit DefaultStateProvider(const std::string& app_name, Browser* browser) | |
20 : app_name_(app_name), | |
21 browser_(browser) { | |
22 } | |
23 | |
24 // Overridden from WindowSizer::StateProvider: | |
25 virtual bool GetPersistentState(gfx::Rect* bounds, | |
26 bool* maximized, | |
27 gfx::Rect* work_area) const { | |
28 DCHECK(bounds && maximized); | |
29 | |
30 std::string key(prefs::kBrowserWindowPlacement); | |
31 if (!app_name_.empty()) { | |
32 key.append("_"); | |
33 key.append(app_name_); | |
34 } | |
35 | |
36 if (!g_browser_process->local_state()) | |
37 return false; | |
38 | |
39 const DictionaryValue* wp_pref = | |
40 g_browser_process->local_state()->GetDictionary(key.c_str()); | |
41 int top = 0, left = 0, bottom = 0, right = 0; | |
42 bool has_prefs = | |
43 wp_pref && | |
44 wp_pref->GetInteger("top", &top) && | |
45 wp_pref->GetInteger("left", &left) && | |
46 wp_pref->GetInteger("bottom", &bottom) && | |
47 wp_pref->GetInteger("right", &right) && | |
48 wp_pref->GetBoolean("maximized", maximized); | |
49 bounds->SetRect(left, top, std::max(0, right - left), | |
50 std::max(0, bottom - top)); | |
51 | |
52 int work_area_top = 0; | |
53 int work_area_left = 0; | |
54 int work_area_bottom = 0; | |
55 int work_area_right = 0; | |
56 if (wp_pref) { | |
57 wp_pref->GetInteger("work_area_top", &work_area_top); | |
58 wp_pref->GetInteger("work_area_left", &work_area_left); | |
59 wp_pref->GetInteger("work_area_bottom", &work_area_bottom); | |
60 wp_pref->GetInteger("work_area_right", &work_area_right); | |
61 } | |
62 work_area->SetRect(work_area_left, work_area_top, | |
63 std::max(0, work_area_right - work_area_left), | |
64 std::max(0, work_area_bottom - work_area_top)); | |
65 | |
66 return has_prefs; | |
67 } | |
68 | |
69 virtual bool GetLastActiveWindowState(gfx::Rect* bounds) const { | |
70 // Applications are always restored with the same position. | |
71 if (!app_name_.empty()) | |
72 return false; | |
73 | |
74 // If a reference browser is set, use its window. Otherwise find last | |
75 // active. | |
76 BrowserWindow* window = NULL; | |
77 if (browser_) { | |
78 window = browser_->window(); | |
79 DCHECK(window); | |
80 } else { | |
81 BrowserList::const_reverse_iterator it = BrowserList::begin_last_active(); | |
82 BrowserList::const_reverse_iterator end = BrowserList::end_last_active(); | |
83 for (; (it != end); ++it) { | |
84 Browser* last_active = *it; | |
85 if (last_active && last_active->type() == Browser::TYPE_NORMAL) { | |
86 window = last_active->window(); | |
87 DCHECK(window); | |
88 break; | |
89 } | |
90 } | |
91 } | |
92 | |
93 if (window) { | |
94 *bounds = window->GetRestoredBounds(); | |
95 return true; | |
96 } | |
97 | |
98 return false; | |
99 } | |
100 | |
101 private: | |
102 std::string app_name_; | |
103 | |
104 // If set, is used as the reference browser for GetLastActiveWindowState. | |
105 Browser* browser_; | |
106 DISALLOW_COPY_AND_ASSIGN(DefaultStateProvider); | |
107 }; | |
108 | |
109 /////////////////////////////////////////////////////////////////////////////// | |
110 // MonitorInfoProvider, public: | |
111 | |
112 WindowSizer::MonitorInfoProvider::MonitorInfoProvider() {} | |
113 | |
114 WindowSizer::MonitorInfoProvider::~MonitorInfoProvider() {} | |
115 | |
116 /////////////////////////////////////////////////////////////////////////////// | |
117 // WindowSizer, public: | |
118 | |
119 WindowSizer::WindowSizer( | |
120 StateProvider* state_provider, | |
121 MonitorInfoProvider* monitor_info_provider) { | |
122 Init(state_provider, monitor_info_provider); | |
123 } | |
124 | |
125 WindowSizer::~WindowSizer() { | |
126 if (state_provider_) | |
127 delete state_provider_; | |
128 if (monitor_info_provider_) | |
129 delete monitor_info_provider_; | |
130 } | |
131 | |
132 // static | |
133 void WindowSizer::GetBrowserWindowBounds(const std::string& app_name, | |
134 const gfx::Rect& specified_bounds, | |
135 Browser* browser, | |
136 gfx::Rect* window_bounds, | |
137 bool* maximized) { | |
138 const WindowSizer sizer(new DefaultStateProvider(app_name, browser), | |
139 CreateDefaultMonitorInfoProvider()); | |
140 sizer.DetermineWindowBounds(specified_bounds, window_bounds, maximized); | |
141 } | |
142 | |
143 /////////////////////////////////////////////////////////////////////////////// | |
144 // WindowSizer, private: | |
145 | |
146 WindowSizer::WindowSizer(const std::string& app_name) { | |
147 Init(new DefaultStateProvider(app_name, NULL), | |
148 CreateDefaultMonitorInfoProvider()); | |
149 } | |
150 | |
151 void WindowSizer::Init(StateProvider* state_provider, | |
152 MonitorInfoProvider* monitor_info_provider) { | |
153 state_provider_ = state_provider; | |
154 monitor_info_provider_ = monitor_info_provider; | |
155 } | |
156 | |
157 void WindowSizer::DetermineWindowBounds(const gfx::Rect& specified_bounds, | |
158 gfx::Rect* bounds, | |
159 bool* maximized) const { | |
160 *bounds = specified_bounds; | |
161 if (bounds->IsEmpty()) { | |
162 // See if there's saved placement information. | |
163 if (!GetLastWindowBounds(bounds)) { | |
164 if (!GetSavedWindowBounds(bounds, maximized)) { | |
165 // No saved placement, figure out some sensible default size based on | |
166 // the user's screen size. | |
167 GetDefaultWindowBounds(bounds); | |
168 } | |
169 } | |
170 } | |
171 } | |
172 | |
173 bool WindowSizer::GetLastWindowBounds(gfx::Rect* bounds) const { | |
174 DCHECK(bounds); | |
175 if (!state_provider_ || !state_provider_->GetLastActiveWindowState(bounds)) | |
176 return false; | |
177 gfx::Rect last_window_bounds = *bounds; | |
178 bounds->Offset(kWindowTilePixels, kWindowTilePixels); | |
179 AdjustBoundsToBeVisibleOnMonitorContaining(last_window_bounds, | |
180 gfx::Rect(), | |
181 bounds); | |
182 return true; | |
183 } | |
184 | |
185 bool WindowSizer::GetSavedWindowBounds(gfx::Rect* bounds, | |
186 bool* maximized) const { | |
187 DCHECK(bounds && maximized); | |
188 gfx::Rect saved_work_area; | |
189 if (!state_provider_ || | |
190 !state_provider_->GetPersistentState(bounds, maximized, &saved_work_area)) | |
191 return false; | |
192 AdjustBoundsToBeVisibleOnMonitorContaining(*bounds, saved_work_area, bounds); | |
193 return true; | |
194 } | |
195 | |
196 void WindowSizer::GetDefaultWindowBounds(gfx::Rect* default_bounds) const { | |
197 DCHECK(default_bounds); | |
198 DCHECK(monitor_info_provider_); | |
199 | |
200 gfx::Rect work_area = monitor_info_provider_->GetPrimaryMonitorWorkArea(); | |
201 | |
202 // The default size is either some reasonably wide width, or if the work | |
203 // area is narrower, then the work area width less some aesthetic padding. | |
204 int default_width = std::min(work_area.width() - 2 * kWindowTilePixels, 1050); | |
205 int default_height = work_area.height() - 2 * kWindowTilePixels; | |
206 | |
207 // For wider aspect ratio displays at higher resolutions, we might size the | |
208 // window narrower to allow two windows to easily be placed side-by-side. | |
209 gfx::Rect screen_size = monitor_info_provider_->GetPrimaryMonitorBounds(); | |
210 double width_to_height = | |
211 static_cast<double>(screen_size.width()) / screen_size.height(); | |
212 | |
213 // The least wide a screen can be to qualify for the halving described above. | |
214 static const int kMinScreenWidthForWindowHalving = 1600; | |
215 // We assume 16:9/10 is a fairly standard indicator of a wide aspect ratio | |
216 // computer display. | |
217 if (((width_to_height * 10) >= 16) && | |
218 work_area.width() > kMinScreenWidthForWindowHalving) { | |
219 // Halve the work area, subtracting aesthetic padding on either side. | |
220 // The padding is set so that two windows, side by side have | |
221 // kWindowTilePixels between screen edge and each other. | |
222 default_width = static_cast<int>(work_area.width() / 2. - | |
223 1.5 * kWindowTilePixels); | |
224 } | |
225 default_bounds->SetRect(kWindowTilePixels + work_area.x(), | |
226 kWindowTilePixels + work_area.y(), | |
227 default_width, default_height); | |
228 } | |
229 | |
230 bool WindowSizer::PositionIsOffscreen(int position, Edge edge) const { | |
231 DCHECK(monitor_info_provider_); | |
232 size_t monitor_count = monitor_info_provider_->GetMonitorCount(); | |
233 for (size_t i = 0; i < monitor_count; ++i) { | |
234 gfx::Rect work_area = monitor_info_provider_->GetWorkAreaAt(i); | |
235 switch (edge) { | |
236 case TOP: | |
237 if (position >= work_area.y()) | |
238 return false; | |
239 break; | |
240 case LEFT: | |
241 if (position >= work_area.x()) | |
242 return false; | |
243 break; | |
244 case BOTTOM: | |
245 if (position <= work_area.bottom()) | |
246 return false; | |
247 break; | |
248 case RIGHT: | |
249 if (position <= work_area.right()) | |
250 return false; | |
251 break; | |
252 } | |
253 } | |
254 return true; | |
255 } | |
256 | |
257 namespace { | |
258 // Minimum height of the visible part of a window. | |
259 static const int kMinVisibleHeight = 30; | |
260 // Minimum width of the visible part of a window. | |
261 static const int kMinVisibleWidth = 30; | |
262 } | |
263 | |
264 void WindowSizer::AdjustBoundsToBeVisibleOnMonitorContaining( | |
265 const gfx::Rect& other_bounds, | |
266 const gfx::Rect& saved_work_area, | |
267 gfx::Rect* bounds) const { | |
268 DCHECK(bounds); | |
269 DCHECK(monitor_info_provider_); | |
270 | |
271 // Find the size of the work area of the monitor that intersects the bounds | |
272 // of the anchor window. | |
273 gfx::Rect work_area = | |
274 monitor_info_provider_->GetMonitorWorkAreaMatching(other_bounds); | |
275 | |
276 // If height or width are 0, reset to the default size. | |
277 gfx::Rect default_bounds; | |
278 GetDefaultWindowBounds(&default_bounds); | |
279 if (bounds->height() <= 0) | |
280 bounds->set_height(default_bounds.height()); | |
281 if (bounds->width() <= 0) | |
282 bounds->set_width(default_bounds.width()); | |
283 | |
284 // Ensure the minimum height and width. | |
285 bounds->set_height(std::max(kMinVisibleHeight, bounds->height())); | |
286 bounds->set_width(std::max(kMinVisibleWidth, bounds->width())); | |
287 | |
288 // Ensure that the title bar is not above the work area. | |
289 if (bounds->y() < work_area.y()) | |
290 bounds->set_y(work_area.y()); | |
291 | |
292 // Reposition and resize the bounds if the saved_work_area is different from | |
293 // the current work area and the current work area doesn't completely contain | |
294 // the bounds. | |
295 if (!saved_work_area.IsEmpty() && | |
296 saved_work_area != work_area && | |
297 !work_area.Contains(*bounds)) { | |
298 bounds->set_width(std::min(bounds->width(), work_area.width())); | |
299 bounds->set_height(std::min(bounds->height(), work_area.height())); | |
300 bounds->set_x( | |
301 std::max(work_area.x(), | |
302 std::min(bounds->x(), work_area.right() - bounds->width()))); | |
303 bounds->set_y( | |
304 std::max(work_area.y(), | |
305 std::min(bounds->y(), work_area.bottom() - bounds->height()))); | |
306 } | |
307 | |
308 #if defined(OS_MACOSX) | |
309 // Limit the maximum height. On the Mac the sizer is on the | |
310 // bottom-right of the window, and a window cannot be moved "up" | |
311 // past the menubar. If the window is too tall you'll never be able | |
312 // to shrink it again. Windows does not have this limitation | |
313 // (e.g. can be resized from the top). | |
314 bounds->set_height(std::min(work_area.height(), bounds->height())); | |
315 | |
316 // On mac, we want to be aggressive about repositioning windows that are | |
317 // partially offscreen. If the window is partially offscreen horizontally, | |
318 // move it to be flush with the left edge of the work area. | |
319 if (bounds->x() < work_area.x() || bounds->right() > work_area.right()) | |
320 bounds->set_x(work_area.x()); | |
321 | |
322 // If the window is partially offscreen vertically, move it to be flush with | |
323 // the top of the work area. | |
324 if (bounds->y() < work_area.y() || bounds->bottom() > work_area.bottom()) | |
325 bounds->set_y(work_area.y()); | |
326 #else | |
327 // On non-Mac platforms, we are less aggressive about repositioning. Simply | |
328 // ensure that at least kMinVisibleWidth * kMinVisibleHeight is visible. | |
329 const int min_y = work_area.y() + kMinVisibleHeight - bounds->height(); | |
330 const int min_x = work_area.x() + kMinVisibleWidth - bounds->width(); | |
331 const int max_y = work_area.bottom() - kMinVisibleHeight; | |
332 const int max_x = work_area.right() - kMinVisibleWidth; | |
333 bounds->set_y(std::max(min_y, std::min(max_y, bounds->y()))); | |
334 bounds->set_x(std::max(min_x, std::min(max_x, bounds->x()))); | |
335 #endif // defined(OS_MACOSX) | |
336 } | |
OLD | NEW |