| 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 |