Componentize AccountReconcilor.
[chromium-blink-merge.git] / chrome / browser / ui / window_sizer / window_sizer.cc
blobf621ab21e323e6b225dc9be7707409c5d17b9fb8
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/window_sizer/window_sizer.h"
7 #include "base/command_line.h"
8 #include "base/compiler_specific.h"
9 #include "base/prefs/pref_service.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_list.h"
14 #include "chrome/browser/ui/browser_window.h"
15 #include "chrome/browser/ui/browser_window_state.h"
16 #include "chrome/browser/ui/host_desktop.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/pref_names.h"
19 #include "ui/gfx/screen.h"
21 #if defined(USE_ASH)
22 #include "ash/shell.h"
23 #include "ash/wm/window_positioner.h"
24 #include "chrome/browser/ui/ash/ash_init.h"
25 #endif
27 namespace {
29 // Minimum height of the visible part of a window.
30 const int kMinVisibleHeight = 30;
31 // Minimum width of the visible part of a window.
32 const int kMinVisibleWidth = 30;
34 ///////////////////////////////////////////////////////////////////////////////
35 // An implementation of WindowSizer::StateProvider that gets the last active
36 // and persistent state from the browser window and the user's profile.
37 class DefaultStateProvider : public WindowSizer::StateProvider {
38 public:
39 DefaultStateProvider(const std::string& app_name, const Browser* browser)
40 : app_name_(app_name), browser_(browser) {
43 // Overridden from WindowSizer::StateProvider:
44 virtual bool GetPersistentState(
45 gfx::Rect* bounds,
46 gfx::Rect* work_area,
47 ui::WindowShowState* show_state) const OVERRIDE {
48 DCHECK(bounds);
49 DCHECK(show_state);
51 if (!browser_ || !browser_->profile()->GetPrefs())
52 return false;
54 std::string window_name(chrome::GetWindowPlacementKey(browser_));
55 const base::DictionaryValue* wp_pref =
56 browser_->profile()->GetPrefs()->GetDictionary(window_name.c_str());
57 int top = 0, left = 0, bottom = 0, right = 0;
58 bool maximized = false;
59 bool has_prefs = wp_pref &&
60 wp_pref->GetInteger("top", &top) &&
61 wp_pref->GetInteger("left", &left) &&
62 wp_pref->GetInteger("bottom", &bottom) &&
63 wp_pref->GetInteger("right", &right) &&
64 wp_pref->GetBoolean("maximized", &maximized);
65 bounds->SetRect(left, top, std::max(0, right - left),
66 std::max(0, bottom - top));
68 int work_area_top = 0;
69 int work_area_left = 0;
70 int work_area_bottom = 0;
71 int work_area_right = 0;
72 if (wp_pref) {
73 wp_pref->GetInteger("work_area_top", &work_area_top);
74 wp_pref->GetInteger("work_area_left", &work_area_left);
75 wp_pref->GetInteger("work_area_bottom", &work_area_bottom);
76 wp_pref->GetInteger("work_area_right", &work_area_right);
77 if (*show_state == ui::SHOW_STATE_DEFAULT && maximized)
78 *show_state = ui::SHOW_STATE_MAXIMIZED;
80 work_area->SetRect(work_area_left, work_area_top,
81 std::max(0, work_area_right - work_area_left),
82 std::max(0, work_area_bottom - work_area_top));
84 return has_prefs;
87 virtual bool GetLastActiveWindowState(
88 gfx::Rect* bounds,
89 ui::WindowShowState* show_state) const OVERRIDE {
90 DCHECK(show_state);
91 // Applications are always restored with the same position.
92 if (!app_name_.empty())
93 return false;
95 // If a reference browser is set, use its window. Otherwise find last
96 // active. Panels are never used as reference browsers as panels are
97 // specially positioned.
98 BrowserWindow* window = NULL;
99 // Window may be null if browser is just starting up.
100 if (browser_ && browser_->window()) {
101 window = browser_->window();
102 } else {
103 // This code is only ran on the native desktop (on the ash
104 // desktop, GetTabbedBrowserBoundsAsh should take over below
105 // before this is reached). TODO(gab): This code should go in a
106 // native desktop specific window sizer as part of fixing
107 // crbug.com/175812.
108 const BrowserList* native_browser_list =
109 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE);
110 for (BrowserList::const_reverse_iterator it =
111 native_browser_list->begin_last_active();
112 it != native_browser_list->end_last_active(); ++it) {
113 Browser* last_active = *it;
114 if (last_active && last_active->is_type_tabbed()) {
115 window = last_active->window();
116 DCHECK(window);
117 break;
122 if (window) {
123 *bounds = window->GetRestoredBounds();
124 if (*show_state == ui::SHOW_STATE_DEFAULT && window->IsMaximized())
125 *show_state = ui::SHOW_STATE_MAXIMIZED;
126 return true;
129 return false;
132 private:
133 std::string app_name_;
135 // If set, is used as the reference browser for GetLastActiveWindowState.
136 const Browser* browser_;
137 DISALLOW_COPY_AND_ASSIGN(DefaultStateProvider);
140 class DefaultTargetDisplayProvider : public WindowSizer::TargetDisplayProvider {
141 public:
142 DefaultTargetDisplayProvider() {}
143 virtual ~DefaultTargetDisplayProvider() {}
145 virtual gfx::Display GetTargetDisplay(
146 const gfx::Screen* screen,
147 const gfx::Rect& bounds) const OVERRIDE {
148 #if defined(USE_ASH)
149 // Use the target display on ash.
150 if (chrome::ShouldOpenAshOnStartup()) {
151 aura::Window* target = ash::Shell::GetTargetRootWindow();
152 return screen->GetDisplayNearestWindow(target);
154 #endif
155 // Find the size of the work area of the monitor that intersects the bounds
156 // of the anchor window.
157 return screen->GetDisplayMatching(bounds);
160 private:
161 DISALLOW_COPY_AND_ASSIGN(DefaultTargetDisplayProvider);
164 } // namespace
166 ///////////////////////////////////////////////////////////////////////////////
167 // WindowSizer, public:
169 WindowSizer::WindowSizer(
170 scoped_ptr<StateProvider> state_provider,
171 scoped_ptr<TargetDisplayProvider> target_display_provider,
172 const Browser* browser)
173 : state_provider_(state_provider.Pass()),
174 target_display_provider_(target_display_provider.Pass()),
175 // TODO(scottmg): NativeScreen is wrong. http://crbug.com/133312
176 screen_(gfx::Screen::GetNativeScreen()),
177 browser_(browser) {
180 WindowSizer::WindowSizer(
181 scoped_ptr<StateProvider> state_provider,
182 scoped_ptr<TargetDisplayProvider> target_display_provider,
183 gfx::Screen* screen,
184 const Browser* browser)
185 : state_provider_(state_provider.Pass()),
186 target_display_provider_(target_display_provider.Pass()),
187 screen_(screen),
188 browser_(browser) {
189 DCHECK(screen_);
192 WindowSizer::~WindowSizer() {
195 // static
196 void WindowSizer::GetBrowserWindowBoundsAndShowState(
197 const std::string& app_name,
198 const gfx::Rect& specified_bounds,
199 const Browser* browser,
200 gfx::Rect* window_bounds,
201 ui::WindowShowState* show_state) {
202 scoped_ptr<StateProvider> state_provider(
203 new DefaultStateProvider(app_name, browser));
204 scoped_ptr<TargetDisplayProvider> target_display_provider(
205 new DefaultTargetDisplayProvider);
206 const WindowSizer sizer(state_provider.Pass(),
207 target_display_provider.Pass(),
208 browser);
209 sizer.DetermineWindowBoundsAndShowState(specified_bounds,
210 window_bounds,
211 show_state);
214 ///////////////////////////////////////////////////////////////////////////////
215 // WindowSizer, private:
217 void WindowSizer::DetermineWindowBoundsAndShowState(
218 const gfx::Rect& specified_bounds,
219 gfx::Rect* bounds,
220 ui::WindowShowState* show_state) const {
221 DCHECK(bounds);
222 DCHECK(show_state);
223 // Pre-populate the window state with our default.
224 *show_state = GetWindowDefaultShowState();
225 *bounds = specified_bounds;
226 if (bounds->IsEmpty()) {
227 #if defined(USE_ASH)
228 // See if ash should decide the window placement.
229 if (IsTabbedBrowserInAsh()) {
230 GetTabbedBrowserBoundsAsh(bounds, show_state);
231 return;
232 } else if (browser_ && browser_->host_desktop_type() ==
233 chrome::HOST_DESKTOP_TYPE_ASH) {
234 // In ash, saved show state takes precidence. If you have a
235 // question or an issue, please contact oshima@chromium.org.
236 GetSavedWindowBounds(bounds, show_state);
238 #endif
239 // See if there's last active window's placement information.
240 if (GetLastActiveWindowBounds(bounds, show_state))
241 return;
242 // See if there's saved placement information.
243 if (GetSavedWindowBounds(bounds, show_state))
244 return;
245 // No saved placement, figure out some sensible default size based on
246 // the user's screen size.
247 GetDefaultWindowBounds(GetTargetDisplay(gfx::Rect()), bounds);
248 } else {
249 #if defined(USE_ASH)
250 // In case of a popup with an 'unspecified' location in ash, we are
251 // looking for a good screen location. We are interpreting (0,0) as an
252 // unspecified location.
253 if (IsPopupBrowserInAsh() && bounds->origin().IsOrigin()) {
254 *bounds = ash::Shell::GetInstance()->window_positioner()->
255 GetPopupPosition(*bounds);
256 return;
258 #endif
259 // In case that there was a bound given we need to make sure that it is
260 // visible and fits on the screen.
261 // Find the size of the work area of the monitor that intersects the bounds
262 // of the anchor window. Note: AdjustBoundsToBeVisibleOnMonitorContaining
263 // does not exactly what we want: It makes only sure that "a minimal part"
264 // is visible on the screen.
265 gfx::Rect work_area = screen_->GetDisplayMatching(*bounds).work_area();
266 // Resize so that it fits.
267 bounds->AdjustToFit(work_area);
271 bool WindowSizer::GetLastActiveWindowBounds(
272 gfx::Rect* bounds,
273 ui::WindowShowState* show_state) const {
274 DCHECK(bounds);
275 DCHECK(show_state);
276 if (!state_provider_.get() ||
277 !state_provider_->GetLastActiveWindowState(bounds, show_state))
278 return false;
279 gfx::Rect last_window_bounds = *bounds;
280 bounds->Offset(kWindowTilePixels, kWindowTilePixels);
281 AdjustBoundsToBeVisibleOnDisplay(screen_->GetDisplayMatching(*bounds),
282 gfx::Rect(),
283 bounds);
284 return true;
287 bool WindowSizer::GetSavedWindowBounds(gfx::Rect* bounds,
288 ui::WindowShowState* show_state) const {
289 DCHECK(bounds);
290 DCHECK(show_state);
291 gfx::Rect saved_work_area;
292 if (!state_provider_.get() ||
293 !state_provider_->GetPersistentState(bounds,
294 &saved_work_area,
295 show_state))
296 return false;
297 AdjustBoundsToBeVisibleOnDisplay(GetTargetDisplay(*bounds),
298 saved_work_area,
299 bounds);
300 return true;
303 void WindowSizer::GetDefaultWindowBounds(const gfx::Display& display,
304 gfx::Rect* default_bounds) const {
305 DCHECK(default_bounds);
306 #if defined(USE_ASH)
307 // TODO(beng): insufficient but currently necessary. http://crbug.com/133312
308 if (chrome::ShouldOpenAshOnStartup()) {
309 *default_bounds = ash::WindowPositioner::GetDefaultWindowBounds(display);
310 return;
312 #endif
313 gfx::Rect work_area = display.work_area();
315 // The default size is either some reasonably wide width, or if the work
316 // area is narrower, then the work area width less some aesthetic padding.
317 int default_width = std::min(work_area.width() - 2 * kWindowTilePixels, 1050);
318 int default_height = work_area.height() - 2 * kWindowTilePixels;
320 // For wider aspect ratio displays at higher resolutions, we might size the
321 // window narrower to allow two windows to easily be placed side-by-side.
322 gfx::Rect screen_size = screen_->GetPrimaryDisplay().bounds();
323 double width_to_height =
324 static_cast<double>(screen_size.width()) / screen_size.height();
326 // The least wide a screen can be to qualify for the halving described above.
327 static const int kMinScreenWidthForWindowHalving = 1600;
328 // We assume 16:9/10 is a fairly standard indicator of a wide aspect ratio
329 // computer display.
330 if (((width_to_height * 10) >= 16) &&
331 work_area.width() > kMinScreenWidthForWindowHalving) {
332 // Halve the work area, subtracting aesthetic padding on either side.
333 // The padding is set so that two windows, side by side have
334 // kWindowTilePixels between screen edge and each other.
335 default_width = static_cast<int>(work_area.width() / 2. -
336 1.5 * kWindowTilePixels);
338 default_bounds->SetRect(kWindowTilePixels + work_area.x(),
339 kWindowTilePixels + work_area.y(),
340 default_width, default_height);
343 void WindowSizer::AdjustBoundsToBeVisibleOnDisplay(
344 const gfx::Display& display,
345 const gfx::Rect& saved_work_area,
346 gfx::Rect* bounds) const {
347 DCHECK(bounds);
349 // If |bounds| is empty, reset to the default size.
350 if (bounds->IsEmpty()) {
351 gfx::Rect default_bounds;
352 GetDefaultWindowBounds(display, &default_bounds);
353 if (bounds->height() <= 0)
354 bounds->set_height(default_bounds.height());
355 if (bounds->width() <= 0)
356 bounds->set_width(default_bounds.width());
359 // Ensure the minimum height and width.
360 bounds->set_height(std::max(kMinVisibleHeight, bounds->height()));
361 bounds->set_width(std::max(kMinVisibleWidth, bounds->width()));
363 gfx::Rect work_area = display.work_area();
364 // Ensure that the title bar is not above the work area.
365 if (bounds->y() < work_area.y())
366 bounds->set_y(work_area.y());
368 // Reposition and resize the bounds if the saved_work_area is different from
369 // the current work area and the current work area doesn't completely contain
370 // the bounds.
371 if (!saved_work_area.IsEmpty() &&
372 saved_work_area != work_area &&
373 !work_area.Contains(*bounds)) {
374 bounds->set_width(std::min(bounds->width(), work_area.width()));
375 bounds->set_height(std::min(bounds->height(), work_area.height()));
376 bounds->set_x(
377 std::max(work_area.x(),
378 std::min(bounds->x(), work_area.right() - bounds->width())));
379 bounds->set_y(
380 std::max(work_area.y(),
381 std::min(bounds->y(), work_area.bottom() - bounds->height())));
384 #if defined(OS_MACOSX)
385 // Limit the maximum height. On the Mac the sizer is on the
386 // bottom-right of the window, and a window cannot be moved "up"
387 // past the menubar. If the window is too tall you'll never be able
388 // to shrink it again. Windows does not have this limitation
389 // (e.g. can be resized from the top).
390 bounds->set_height(std::min(work_area.height(), bounds->height()));
392 // On mac, we want to be aggressive about repositioning windows that are
393 // partially offscreen. If the window is partially offscreen horizontally,
394 // move it to be flush with the left edge of the work area.
395 if (bounds->x() < work_area.x() || bounds->right() > work_area.right())
396 bounds->set_x(work_area.x());
398 // If the window is partially offscreen vertically, move it to be flush with
399 // the top of the work area.
400 if (bounds->y() < work_area.y() || bounds->bottom() > work_area.bottom())
401 bounds->set_y(work_area.y());
402 #else
403 // On non-Mac platforms, we are less aggressive about repositioning. Simply
404 // ensure that at least kMinVisibleWidth * kMinVisibleHeight is visible.
405 const int min_y = work_area.y() + kMinVisibleHeight - bounds->height();
406 const int min_x = work_area.x() + kMinVisibleWidth - bounds->width();
407 const int max_y = work_area.bottom() - kMinVisibleHeight;
408 const int max_x = work_area.right() - kMinVisibleWidth;
409 bounds->set_y(std::max(min_y, std::min(max_y, bounds->y())));
410 bounds->set_x(std::max(min_x, std::min(max_x, bounds->x())));
411 #endif // defined(OS_MACOSX)
414 gfx::Display WindowSizer::GetTargetDisplay(const gfx::Rect& bounds) const {
415 return target_display_provider_->GetTargetDisplay(screen_, bounds);
418 ui::WindowShowState WindowSizer::GetWindowDefaultShowState() const {
419 if (!browser_)
420 return ui::SHOW_STATE_DEFAULT;
422 // Only tabbed browsers use the command line or preference state, with the
423 // exception of devtools.
424 bool show_state = !browser_->is_type_tabbed() && !browser_->is_devtools();
426 #if defined(USE_AURA)
427 // We use the apps save state on aura.
428 show_state &= !browser_->is_app();
429 #endif
431 if (show_state)
432 return browser_->initial_show_state();
434 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kStartMaximized))
435 return ui::SHOW_STATE_MAXIMIZED;
437 if (browser_->initial_show_state() != ui::SHOW_STATE_DEFAULT)
438 return browser_->initial_show_state();
440 // Otherwise we use the default which can be overridden later on.
441 return ui::SHOW_STATE_DEFAULT;
444 #if defined(USE_ASH)
445 bool WindowSizer::IsTabbedBrowserInAsh() const {
446 return browser_ &&
447 browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH &&
448 browser_->is_type_tabbed();
451 bool WindowSizer::IsPopupBrowserInAsh() const {
452 return browser_ &&
453 browser_->host_desktop_type() == chrome::HOST_DESKTOP_TYPE_ASH &&
454 browser_->is_type_popup();
456 #endif