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