1 // Copyright (c) 2006-2008 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/window_sizer.h"
7 #include "chrome/browser/browser.h"
8 #include "chrome/browser/browser_list.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/browser_type.h"
11 #include "chrome/browser/browser_window.h"
12 #include "chrome/common/pref_names.h"
13 #include "chrome/common/pref_service.h"
15 // How much horizontal and vertical offset there is between newly opened
17 static const int kWindowTilePixels
= 10;
19 ///////////////////////////////////////////////////////////////////////////////
20 // An implementation of WindowSizer::MonitorInfoProvider that gets the actual
21 // monitor information from Windows.
22 class DefaultMonitorInfoProvider
: public WindowSizer::MonitorInfoProvider
{
24 DefaultMonitorInfoProvider() {
25 EnumDisplayMonitors(NULL
, NULL
,
26 &DefaultMonitorInfoProvider::MonitorEnumProc
,
27 reinterpret_cast<LPARAM
>(&working_rects_
));
30 // Overridden from WindowSizer::MonitorInfoProvider:
31 virtual gfx::Rect
GetPrimaryMonitorWorkingRect() const {
32 HMONITOR monitor
= MonitorFromWindow(NULL
, MONITOR_DEFAULTTOPRIMARY
);
33 MONITORINFO monitor_info
;
34 monitor_info
.cbSize
= sizeof(monitor_info
);
35 GetMonitorInfo(monitor
, &monitor_info
);
36 return gfx::Rect(monitor_info
.rcWork
);
39 virtual gfx::Rect
GetPrimaryMonitorBounds() const {
40 HMONITOR monitor
= MonitorFromWindow(NULL
, MONITOR_DEFAULTTOPRIMARY
);
41 MONITORINFO monitor_info
;
42 monitor_info
.cbSize
= sizeof(monitor_info
);
43 GetMonitorInfo(monitor
, &monitor_info
);
44 return gfx::Rect(monitor_info
.rcMonitor
);
47 virtual gfx::Rect
GetMonitorBoundsMatching(
48 const gfx::Rect
& match_rect
) const {
49 CRect other_bounds_crect
= match_rect
.ToRECT();
51 MonitorFromRect(&other_bounds_crect
, MONITOR_DEFAULTTOPRIMARY
);
52 MONITORINFO monitor_info
;
53 monitor_info
.cbSize
= sizeof(monitor_info
);
54 GetMonitorInfo(monitor
, &monitor_info
);
55 return gfx::Rect(monitor_info
.rcWork
);
58 virtual gfx::Point
GetBoundsOffsetMatching(
59 const gfx::Rect
& match_rect
) const {
60 CRect other_bounds_crect
= match_rect
.ToRECT();
62 MonitorFromRect(&other_bounds_crect
, MONITOR_DEFAULTTOPRIMARY
);
63 MONITORINFO monitor_info
;
64 monitor_info
.cbSize
= sizeof(monitor_info
);
65 GetMonitorInfo(monitor
, &monitor_info
);
66 return gfx::Point(monitor_info
.rcWork
.left
- monitor_info
.rcMonitor
.left
,
67 monitor_info
.rcWork
.top
- monitor_info
.rcMonitor
.top
);
70 virtual int GetMonitorCount() const {
71 return static_cast<int>(working_rects_
.size());
74 virtual gfx::Rect
GetWorkingRectAt(int index
) const {
75 DCHECK(index
>= 0 && index
< GetMonitorCount());
76 return working_rects_
.at(index
);
80 // A callback for EnumDisplayMonitors that records the work area of the
81 // current monitor in the enumeration.
82 static BOOL CALLBACK
MonitorEnumProc(HMONITOR monitor
,
86 std::vector
<gfx::Rect
>* working_rects
=
87 reinterpret_cast<std::vector
<gfx::Rect
>*>(data
);
89 info
.cbSize
= sizeof(info
);
90 GetMonitorInfo(monitor
, &info
);
91 working_rects
->push_back(gfx::Rect(info
.rcWork
));
95 std::vector
<gfx::Rect
> working_rects_
;
97 DISALLOW_EVIL_CONSTRUCTORS(DefaultMonitorInfoProvider
);
100 ///////////////////////////////////////////////////////////////////////////////
101 // An implementation of WindowSizer::StateProvider that gets the last active
102 // and persistent state from the browser window and the user's profile.
103 class DefaultStateProvider
: public WindowSizer::StateProvider
{
105 explicit DefaultStateProvider(const std::wstring
& app_name
)
106 : app_name_(app_name
) {
109 // Overridden from WindowSizer::StateProvider:
110 virtual bool GetPersistentState(gfx::Rect
* bounds
, bool* maximized
) const {
111 DCHECK(bounds
&& maximized
);
113 std::wstring
key(prefs::kBrowserWindowPlacement
);
114 if (!app_name_
.empty()) {
116 key
.append(app_name_
);
119 const DictionaryValue
* wp_pref
=
120 g_browser_process
->local_state()->GetDictionary(key
.c_str());
121 int top
= 0, left
= 0, bottom
= 0, right
= 0;
124 wp_pref
->GetInteger(L
"top", &top
) &&
125 wp_pref
->GetInteger(L
"left", &left
) &&
126 wp_pref
->GetInteger(L
"bottom", &bottom
) &&
127 wp_pref
->GetInteger(L
"right", &right
) &&
128 wp_pref
->GetBoolean(L
"maximized", maximized
);
129 bounds
->SetRect(left
, top
, std::max(0, right
- left
),
130 std::max(0, bottom
- top
));
134 virtual bool GetLastActiveWindowState(gfx::Rect
* bounds
) const {
135 // Applications are always restored with the same position.
136 if (!app_name_
.empty())
139 BrowserList::const_reverse_iterator it
= BrowserList::begin_last_active();
140 BrowserList::const_reverse_iterator end
= BrowserList::end_last_active();
141 for (; it
!= end
; ++it
) {
142 Browser
* last_active
= *it
;
144 last_active
->GetType() == BrowserType::TABBED_BROWSER
) {
145 BrowserWindow
* frame
= last_active
->window();
147 *bounds
= frame
->GetNormalBounds();
156 std::wstring app_name_
;
158 DISALLOW_EVIL_CONSTRUCTORS(DefaultStateProvider
);
161 ///////////////////////////////////////////////////////////////////////////////
162 // WindowSizer, public:
164 WindowSizer::WindowSizer(
165 StateProvider
* state_provider
,
166 MonitorInfoProvider
* monitor_info_provider
) {
167 Init(state_provider
, monitor_info_provider
);
170 WindowSizer::~WindowSizer() {
172 delete state_provider_
;
173 if (monitor_info_provider_
)
174 delete monitor_info_provider_
;
178 void WindowSizer::GetBrowserWindowBounds(const std::wstring
& app_name
,
179 const gfx::Rect
& specified_bounds
,
180 gfx::Rect
* window_bounds
,
182 const WindowSizer
sizer(new DefaultStateProvider(app_name
),
183 new DefaultMonitorInfoProvider
);
184 sizer
.DetermineWindowBounds(specified_bounds
, window_bounds
, maximized
);
188 ///////////////////////////////////////////////////////////////////////////////
189 // WindowSizer, private:
191 WindowSizer::WindowSizer(const std::wstring
& app_name
) {
192 Init(new DefaultStateProvider(app_name
),
193 new DefaultMonitorInfoProvider
);
196 void WindowSizer::Init(StateProvider
* state_provider
,
197 MonitorInfoProvider
* monitor_info_provider
) {
198 state_provider_
= state_provider
;
199 monitor_info_provider_
= monitor_info_provider
;
202 void WindowSizer::DetermineWindowBounds(const gfx::Rect
& specified_bounds
,
204 bool* maximized
) const {
205 *bounds
= specified_bounds
;
206 if (bounds
->IsEmpty()) {
207 // See if there's saved placement information.
208 *maximized
= false; // Default off; GetSavedWindowBounds() may set this.
209 if (!GetLastWindowBounds(bounds
)) {
210 if (!GetSavedWindowBounds(bounds
, maximized
)) {
211 // No saved placement, figure out some sensible default size based on
212 // the user's screen size.
213 GetDefaultWindowBounds(bounds
);
219 bool WindowSizer::GetLastWindowBounds(gfx::Rect
* bounds
) const {
221 if (!state_provider_
|| !state_provider_
->GetLastActiveWindowState(bounds
))
223 gfx::Rect last_window_bounds
= *bounds
;
224 bounds
->Offset(kWindowTilePixels
, kWindowTilePixels
);
225 AdjustBoundsToBeVisibleOnMonitorContaining(last_window_bounds
, bounds
);
229 bool WindowSizer::GetSavedWindowBounds(gfx::Rect
* bounds
,
230 bool* maximized
) const {
231 DCHECK(bounds
&& maximized
);
232 if (!state_provider_
||
233 !state_provider_
->GetPersistentState(bounds
, maximized
))
235 const gfx::Point
& taskbar_offset
=
236 monitor_info_provider_
->GetBoundsOffsetMatching(*bounds
);
237 bounds
->Offset(taskbar_offset
.x(), taskbar_offset
.y());
238 AdjustBoundsToBeVisibleOnMonitorContaining(*bounds
, bounds
);
242 void WindowSizer::GetDefaultWindowBounds(gfx::Rect
* default_bounds
) const {
243 DCHECK(default_bounds
);
244 DCHECK(monitor_info_provider_
);
246 gfx::Rect work_rect
= monitor_info_provider_
->GetPrimaryMonitorWorkingRect();
248 // The default size is either some reasonably wide width, or if the work
249 // area is narrower, then the work area width less some aesthetic padding.
250 int default_width
= std::min(work_rect
.width() - 2 * kWindowTilePixels
, 1050);
251 int default_height
= work_rect
.height() - 2 * kWindowTilePixels
;
253 // For wider aspect ratio displays at higher resolutions, we might size the
254 // window narrower to allow two windows to easily be placed side-by-side.
255 gfx::Rect screen_size
= monitor_info_provider_
->GetPrimaryMonitorBounds();
256 double width_to_height
=
257 static_cast<double>(screen_size
.width()) / screen_size
.height();
259 // The least wide a screen can be to qualify for the halving described above.
260 static const int kMinScreenWidthForWindowHalving
= 1600;
261 // We assume 16:9/10 is a fairly standard indicator of a wide aspect ratio
263 if (((width_to_height
* 10) >= 16) &&
264 work_rect
.width() > kMinScreenWidthForWindowHalving
) {
265 // Halve the work area, subtracting aesthetic padding on either side, plus
266 // some more aesthetic padding for spacing between windows.
267 default_width
= (work_rect
.width() / 2) - 3 * kWindowTilePixels
;
269 default_bounds
->SetRect(kWindowTilePixels
+ work_rect
.x(),
270 kWindowTilePixels
+ work_rect
.y(),
271 default_width
, default_height
);
274 bool WindowSizer::PositionIsOffscreen(int position
, Edge edge
) const {
275 DCHECK(monitor_info_provider_
);
277 int monitor_count
= monitor_info_provider_
->GetMonitorCount();
278 for (int i
= 0; i
< monitor_count
; ++i
) {
279 gfx::Rect working_rect
= monitor_info_provider_
->GetWorkingRectAt(i
);
282 if (position
>= working_rect
.y())
286 if (position
>= working_rect
.x())
290 if (position
<= working_rect
.height())
294 if (position
<= working_rect
.width())
302 void WindowSizer::AdjustBoundsToBeVisibleOnMonitorContaining(
303 const gfx::Rect
& other_bounds
, gfx::Rect
* bounds
) const {
305 DCHECK(monitor_info_provider_
);
307 // Find the size of the work area of the monitor that intersects the bounds
308 // of the anchor window.
309 gfx::Rect work_area
=
310 monitor_info_provider_
->GetMonitorBoundsMatching(other_bounds
);
312 // If height or width are 0, reset to the default size.
313 gfx::Rect default_bounds
;
314 GetDefaultWindowBounds(&default_bounds
);
315 if (bounds
->height() <= 0)
316 bounds
->set_height(default_bounds
.height());
317 if (bounds
->width() <= 0)
318 bounds
->set_width(default_bounds
.width());
320 // First determine which screen edge(s) the window is offscreen on.
321 bool top_offscreen
= !PositionIsOffscreen(bounds
->y(), TOP
);
322 bool left_offscreen
= !PositionIsOffscreen(bounds
->x(), LEFT
);
323 bool bottom_offscreen
= !PositionIsOffscreen(bounds
->bottom(), BOTTOM
);
324 bool right_offscreen
= !PositionIsOffscreen(bounds
->right(), RIGHT
);
326 // Bump the window back onto the screen in the direction that it's offscreen.
327 if (bottom_offscreen
) {
328 int y
= work_area
.bottom() - kWindowTilePixels
- bounds
->height();
329 bounds
->set_y(std::max(kWindowTilePixels
, y
));
331 if (right_offscreen
) {
332 int x
= work_area
.right() - kWindowTilePixels
- bounds
->width();
333 bounds
->set_x(std::max(kWindowTilePixels
, x
));
336 bounds
->set_y(kWindowTilePixels
+ work_area
.y());
338 bounds
->set_x(kWindowTilePixels
+ work_area
.x());
340 // Now that we've tried to correct the x/y position to something reasonable,
341 // see if the window is still too tall or wide to fit, and resize it if need
343 if ((bottom_offscreen
|| top_offscreen
) &&
344 bounds
->bottom() > work_area
.bottom()) {
345 bounds
->set_height(work_area
.height() - 2 * kWindowTilePixels
);
347 if ((left_offscreen
|| right_offscreen
) &&
348 bounds
->right() > work_area
.right()) {
349 bounds
->set_width(work_area
.width() - 2 * kWindowTilePixels
);