summaryrefslogtreecommitdiffstats
path: root/chrome/browser/window_sizer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/window_sizer.cc')
-rw-r--r--chrome/browser/window_sizer.cc376
1 files changed, 376 insertions, 0 deletions
diff --git a/chrome/browser/window_sizer.cc b/chrome/browser/window_sizer.cc
new file mode 100644
index 0000000..b654e4c
--- /dev/null
+++ b/chrome/browser/window_sizer.cc
@@ -0,0 +1,376 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/window_sizer.h"
+
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_type.h"
+#include "chrome/browser/chrome_frame.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+
+// How much horizontal and vertical offset there is between newly opened
+// windows.
+static const int kWindowTilePixels = 10;
+
+///////////////////////////////////////////////////////////////////////////////
+// An implementation of WindowSizer::MonitorInfoProvider that gets the actual
+// monitor information from Windows.
+class DefaultMonitorInfoProvider : public WindowSizer::MonitorInfoProvider {
+ public:
+ DefaultMonitorInfoProvider() {
+ EnumDisplayMonitors(NULL, NULL,
+ &DefaultMonitorInfoProvider::MonitorEnumProc,
+ reinterpret_cast<LPARAM>(&working_rects_));
+ }
+
+ // Overridden from WindowSizer::MonitorInfoProvider:
+ virtual gfx::Rect GetPrimaryMonitorWorkingRect() const {
+ HMONITOR monitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(monitor_info);
+ GetMonitorInfo(monitor, &monitor_info);
+ return gfx::Rect(monitor_info.rcWork);
+ }
+
+ virtual gfx::Rect GetPrimaryMonitorBounds() const {
+ HMONITOR monitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(monitor_info);
+ GetMonitorInfo(monitor, &monitor_info);
+ return gfx::Rect(monitor_info.rcMonitor);
+ }
+
+ virtual gfx::Rect GetMonitorBoundsMatching(
+ const gfx::Rect& match_rect) const {
+ CRect other_bounds_crect = match_rect.ToRECT();
+ HMONITOR monitor =
+ MonitorFromRect(&other_bounds_crect, MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(monitor_info);
+ GetMonitorInfo(monitor, &monitor_info);
+ return gfx::Rect(monitor_info.rcWork);
+ }
+
+ virtual gfx::Point GetBoundsOffsetMatching(
+ const gfx::Rect& match_rect) const {
+ CRect other_bounds_crect = match_rect.ToRECT();
+ HMONITOR monitor =
+ MonitorFromRect(&other_bounds_crect, MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(monitor_info);
+ GetMonitorInfo(monitor, &monitor_info);
+ return gfx::Point(monitor_info.rcWork.left - monitor_info.rcMonitor.left,
+ monitor_info.rcWork.top - monitor_info.rcMonitor.top);
+ }
+
+ virtual int GetMonitorCount() const {
+ return static_cast<int>(working_rects_.size());
+ }
+
+ virtual gfx::Rect GetWorkingRectAt(int index) const {
+ DCHECK(index >= 0 && index < GetMonitorCount());
+ return working_rects_.at(index);
+ }
+
+ private:
+ // A callback for EnumDisplayMonitors that records the work area of the
+ // current monitor in the enumeration.
+ static BOOL CALLBACK MonitorEnumProc(HMONITOR monitor,
+ HDC monitor_dc,
+ LPRECT monitor_rect,
+ LPARAM data) {
+ std::vector<gfx::Rect>* working_rects =
+ reinterpret_cast<std::vector<gfx::Rect>*>(data);
+ MONITORINFO info;
+ info.cbSize = sizeof(info);
+ GetMonitorInfo(monitor, &info);
+ working_rects->push_back(gfx::Rect(info.rcWork));
+ return TRUE;
+ }
+
+ std::vector<gfx::Rect> working_rects_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DefaultMonitorInfoProvider);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// An implementation of WindowSizer::StateProvider that gets the last active
+// and persistent state from the browser window and the user's profile.
+class DefaultStateProvider : public WindowSizer::StateProvider {
+ public:
+ explicit DefaultStateProvider(const std::wstring& app_name)
+ : app_name_(app_name) {
+ }
+
+ // Overridden from WindowSizer::StateProvider:
+ virtual bool GetPersistentState(gfx::Rect* bounds, bool* maximized) const {
+ DCHECK(bounds && maximized);
+
+ std::wstring key(prefs::kBrowserWindowPlacement);
+ if (!app_name_.empty()) {
+ key.append(L"_");
+ key.append(app_name_);
+ }
+
+ const DictionaryValue* wp_pref =
+ g_browser_process->local_state()->GetDictionary(key.c_str());
+ int top = 0, left = 0, bottom = 0, right = 0;
+ bool has_prefs =
+ wp_pref &&
+ wp_pref->GetInteger(L"top", &top) &&
+ wp_pref->GetInteger(L"left", &left) &&
+ wp_pref->GetInteger(L"bottom", &bottom) &&
+ wp_pref->GetInteger(L"right", &right) &&
+ wp_pref->GetBoolean(L"maximized", maximized);
+ bounds->SetRect(left, top, std::max(0, right - left),
+ std::max(0, bottom - top));
+ return has_prefs;
+ }
+
+ virtual bool GetLastActiveWindowState(gfx::Rect* bounds) const {
+ // Applications are always restored with the same position.
+ if (!app_name_.empty())
+ return false;
+
+ BrowserList::const_reverse_iterator it = BrowserList::begin_last_active();
+ BrowserList::const_reverse_iterator end = BrowserList::end_last_active();
+ for (; it != end; ++it) {
+ Browser* last_active = *it;
+ if (last_active &&
+ last_active->GetType() == BrowserType::TABBED_BROWSER) {
+ ChromeFrame* frame = last_active->frame();
+ DCHECK(frame);
+ *bounds = frame->GetNormalBounds();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private:
+ std::wstring app_name_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DefaultStateProvider);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// WindowSizer, public:
+
+WindowSizer::WindowSizer(
+ StateProvider* state_provider,
+ MonitorInfoProvider* monitor_info_provider) {
+ Init(state_provider, monitor_info_provider);
+}
+
+WindowSizer::~WindowSizer() {
+ if (state_provider_)
+ delete state_provider_;
+ if (monitor_info_provider_)
+ delete monitor_info_provider_;
+}
+
+// static
+void WindowSizer::GetBrowserWindowBounds(const std::wstring& app_name,
+ const gfx::Rect& specified_bounds,
+ gfx::Rect* window_bounds,
+ bool* maximized) {
+ const WindowSizer sizer(new DefaultStateProvider(app_name),
+ new DefaultMonitorInfoProvider);
+ sizer.DetermineWindowBounds(specified_bounds, window_bounds, maximized);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// WindowSizer, private:
+
+WindowSizer::WindowSizer(const std::wstring& app_name) {
+ Init(new DefaultStateProvider(app_name),
+ new DefaultMonitorInfoProvider);
+}
+
+void WindowSizer::Init(StateProvider* state_provider,
+ MonitorInfoProvider* monitor_info_provider) {
+ state_provider_ = state_provider;
+ monitor_info_provider_ = monitor_info_provider;
+}
+
+void WindowSizer::DetermineWindowBounds(const gfx::Rect& specified_bounds,
+ gfx::Rect* bounds,
+ bool* maximized) const {
+ *bounds = specified_bounds;
+ if (bounds->IsEmpty()) {
+ // See if there's saved placement information.
+ *maximized = false; // Default off; GetSavedWindowBounds() may set this.
+ if (!GetLastWindowBounds(bounds)) {
+ if (!GetSavedWindowBounds(bounds, maximized)) {
+ // No saved placement, figure out some sensible default size based on
+ // the user's screen size.
+ GetDefaultWindowBounds(bounds);
+ }
+ }
+ }
+}
+
+bool WindowSizer::GetLastWindowBounds(gfx::Rect* bounds) const {
+ DCHECK(bounds);
+ if (!state_provider_ || !state_provider_->GetLastActiveWindowState(bounds))
+ return false;
+ gfx::Rect last_window_bounds = *bounds;
+ bounds->Offset(kWindowTilePixels, kWindowTilePixels);
+ AdjustBoundsToBeVisibleOnMonitorContaining(last_window_bounds, bounds);
+ return true;
+}
+
+bool WindowSizer::GetSavedWindowBounds(gfx::Rect* bounds,
+ bool* maximized) const {
+ DCHECK(bounds && maximized);
+ if (!state_provider_ ||
+ !state_provider_->GetPersistentState(bounds, maximized))
+ return false;
+ const gfx::Point& taskbar_offset =
+ monitor_info_provider_->GetBoundsOffsetMatching(*bounds);
+ bounds->Offset(taskbar_offset.x(), taskbar_offset.y());
+ AdjustBoundsToBeVisibleOnMonitorContaining(*bounds, bounds);
+ return true;
+}
+
+void WindowSizer::GetDefaultWindowBounds(gfx::Rect* default_bounds) const {
+ DCHECK(default_bounds);
+ DCHECK(monitor_info_provider_);
+
+ gfx::Rect work_rect = monitor_info_provider_->GetPrimaryMonitorWorkingRect();
+
+ // The default size is either some reasonably wide width, or if the work
+ // area is narrower, then the work area width less some aesthetic padding.
+ int default_width = std::min(work_rect.width() - 2 * kWindowTilePixels, 1050);
+ int default_height = work_rect.height() - 2 * kWindowTilePixels;
+
+ // For wider aspect ratio displays at higher resolutions, we might size the
+ // window narrower to allow two windows to easily be placed side-by-side.
+ gfx::Rect screen_size = monitor_info_provider_->GetPrimaryMonitorBounds();
+ double width_to_height =
+ static_cast<double>(screen_size.width()) / screen_size.height();
+
+ // The least wide a screen can be to qualify for the halving described above.
+ static const int kMinScreenWidthForWindowHalving = 1600;
+ // We assume 16:9/10 is a fairly standard indicator of a wide aspect ratio
+ // computer display.
+ if (((width_to_height * 10) >= 16) &&
+ work_rect.width() > kMinScreenWidthForWindowHalving) {
+ // Halve the work area, subtracting aesthetic padding on either side, plus
+ // some more aesthetic padding for spacing between windows.
+ default_width = (work_rect.width() / 2) - 3 * kWindowTilePixels;
+ }
+ default_bounds->SetRect(kWindowTilePixels + work_rect.x(),
+ kWindowTilePixels + work_rect.y(),
+ default_width, default_height);
+}
+
+bool WindowSizer::PositionIsOffscreen(int position, Edge edge) const {
+ DCHECK(monitor_info_provider_);
+
+ int monitor_count = monitor_info_provider_->GetMonitorCount();
+ for (int i = 0; i < monitor_count; ++i) {
+ gfx::Rect working_rect = monitor_info_provider_->GetWorkingRectAt(i);
+ switch (edge) {
+ case TOP:
+ if (position >= working_rect.y())
+ return true;
+ break;
+ case LEFT:
+ if (position >= working_rect.x())
+ return true;
+ break;
+ case BOTTOM:
+ if (position <= working_rect.height())
+ return true;
+ break;
+ case RIGHT:
+ if (position <= working_rect.width())
+ return true;
+ break;
+ }
+ }
+ return false;
+}
+
+void WindowSizer::AdjustBoundsToBeVisibleOnMonitorContaining(
+ const gfx::Rect& other_bounds, gfx::Rect* bounds) const {
+ DCHECK(bounds);
+ DCHECK(monitor_info_provider_);
+
+ // Find the size of the work area of the monitor that intersects the bounds
+ // of the anchor window.
+ gfx::Rect work_area =
+ monitor_info_provider_->GetMonitorBoundsMatching(other_bounds);
+
+ // If height or width are 0, reset to the default size.
+ gfx::Rect default_bounds;
+ GetDefaultWindowBounds(&default_bounds);
+ if (bounds->height() <= 0)
+ bounds->set_height(default_bounds.height());
+ if (bounds->width() <= 0)
+ bounds->set_width(default_bounds.width());
+
+ // First determine which screen edge(s) the window is offscreen on.
+ bool top_offscreen = !PositionIsOffscreen(bounds->y(), TOP);
+ bool left_offscreen = !PositionIsOffscreen(bounds->x(), LEFT);
+ bool bottom_offscreen = !PositionIsOffscreen(bounds->bottom(), BOTTOM);
+ bool right_offscreen = !PositionIsOffscreen(bounds->right(), RIGHT);
+
+ // Bump the window back onto the screen in the direction that it's offscreen.
+ if (bottom_offscreen) {
+ int y = work_area.bottom() - kWindowTilePixels - bounds->height();
+ bounds->set_y(std::max(kWindowTilePixels, y));
+ }
+ if (right_offscreen) {
+ int x = work_area.right() - kWindowTilePixels - bounds->width();
+ bounds->set_x(std::max(kWindowTilePixels, x));
+ }
+ if (top_offscreen)
+ bounds->set_y(kWindowTilePixels + work_area.y());
+ if (left_offscreen)
+ bounds->set_x(kWindowTilePixels + work_area.x());
+
+ // Now that we've tried to correct the x/y position to something reasonable,
+ // see if the window is still too tall or wide to fit, and resize it if need
+ // be.
+ if ((bottom_offscreen || top_offscreen) &&
+ bounds->bottom() > work_area.bottom()) {
+ bounds->set_height(work_area.height() - 2 * kWindowTilePixels);
+ }
+ if ((left_offscreen || right_offscreen) &&
+ bounds->right() > work_area.right()) {
+ bounds->set_width(work_area.width() - 2 * kWindowTilePixels);
+ }
+}