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: 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: 26 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 70 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 113 WindowSizer::MonitorInfoProvider::MonitorInfoProvider() {} 114 115 WindowSizer::MonitorInfoProvider::~MonitorInfoProvider() {} 116 117 /////////////////////////////////////////////////////////////////////////////// 118 // WindowSizer, public: 119 120 WindowSizer::WindowSizer( 121 StateProvider* state_provider, 122 MonitorInfoProvider* monitor_info_provider) { 123 Init(state_provider, monitor_info_provider); 124 } 125 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 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 147 WindowSizer::WindowSizer(const std::string& app_name) { 148 Init(new DefaultStateProvider(app_name, NULL), 149 CreateDefaultMonitorInfoProvider()); 150 } 151 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 158 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 174 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 186 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 197 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 231 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 265 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