blob: f9c4d1f27cc4c34c6c471e32bbc3989d5a7e85a9 [file] [log] [blame]
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/views/tabs/tab_overview_controller.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/thumbnail_generator.h"
#include "chrome/browser/views/frame/browser_extender.h"
#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/browser/views/tabs/tab_overview_cell.h"
#include "chrome/browser/views/tabs/tab_overview_container.h"
#include "chrome/browser/views/tabs/tab_overview_drag_controller.h"
#include "chrome/browser/views/tabs/tab_overview_grid.h"
#include "chrome/browser/views/tabs/tab_overview_types.h"
#include "chrome/browser/window_sizer.h"
#include "chrome/common/x11_util.h"
#include "views/widget/root_view.h"
#include "views/widget/widget_gtk.h"
#include "views/window/window.h"
// Horizontal padding from the edge of the monitor to the overview.
static int kMonitorPadding = 20;
// Vertical padding between the overview and the windows along on the bottom.
static int kWindowToOverviewPadding = 25;
// Height of the windows along the bottom, as a percentage of the monitors
// height.
static float kWindowHeight = .30;
// Height of the tab overview, as a percentage of monitors height.
static float kOverviewHeight = .55;
TabOverviewController::TabOverviewController(
const gfx::Point& monitor_origin)
: host_(NULL),
container_(NULL),
grid_(NULL),
browser_(NULL),
drag_browser_(NULL),
moved_offscreen_(false),
shown_(false),
horizontal_center_(0),
change_window_bounds_on_animate_(false),
mutating_grid_(false),
show_thumbnails_(false) {
grid_ = new TabOverviewGrid(this);
// Determine the max size for the overview.
scoped_ptr<WindowSizer::MonitorInfoProvider> provider(
WindowSizer::CreateDefaultMonitorInfoProvider());
monitor_bounds_ = provider->GetMonitorWorkAreaMatching(
gfx::Rect(monitor_origin.x(), monitor_origin.y(), 1, 1));
// Create the host.
views::WidgetGtk* host = new views::WidgetGtk(views::WidgetGtk::TYPE_POPUP);
host->set_delete_on_destroy(false);
host->MakeTransparent();
host->Init(NULL, CalculateHostBounds());
TabOverviewTypes::instance()->SetWindowType(
host->GetNativeView(),
TabOverviewTypes::WINDOW_TYPE_CHROME_TAB_SUMMARY,
NULL);
host_ = host;
container_ = new TabOverviewContainer();
container_->AddChildView(grid_);
host->GetRootView()->AddChildView(container_);
container_->SetMaxSize(CalculateHostBounds().size());
horizontal_center_ = monitor_bounds_.x() + monitor_bounds_.width() / 2;
}
TabOverviewController::~TabOverviewController() {
if (browser_)
model()->RemoveObserver(this);
host_->Close();
// The drag controller may call back to us from it's destructor. Make sure
// it's destroyed before us.
grid()->CancelDrag();
}
void TabOverviewController::SetBrowser(Browser* browser,
int horizontal_center) {
horizontal_center_ = horizontal_center;
if (browser_)
model()->RemoveObserver(this);
browser_ = browser;
if (browser_)
model()->AddObserver(this);
show_thumbnails_ = false;
StartDelayTimer();
gfx::Rect host_bounds = CalculateHostBounds();
if (moved_offscreen_ && model() && model()->count()) {
// Need to reset the bounds if we were offscreen.
host_->SetBounds(host_bounds);
moved_offscreen_ = false;
} else if (!model() && shown_) {
MoveOffscreen();
}
if (!moved_offscreen_)
container_->SchedulePaint();
RecreateCells();
container_->set_arrow_center(horizontal_center_ - host_bounds.x());
if (!moved_offscreen_)
container_->SchedulePaint();
}
TabStripModel* TabOverviewController::model() const {
return browser_ ? browser_->tabstrip_model() : NULL;
}
void TabOverviewController::SetMouseOverMiniWindow(bool over_mini_window) {
if (grid_->drag_controller())
grid_->drag_controller()->set_mouse_over_mini_window(over_mini_window);
}
void TabOverviewController::Show() {
if (host_->IsVisible())
return;
shown_ = true;
DCHECK(model()); // The model needs to be set before showing.
host_->Show();
show_thumbnails_ = false;
StartDelayTimer();
}
void TabOverviewController::ConfigureCell(TabOverviewCell* cell,
TabContents* contents) {
if (contents) {
cell->SetTitle(contents->GetTitle());
cell->SetFavIcon(contents->GetFavIcon());
if (show_thumbnails_) {
ThumbnailGenerator* generator =
g_browser_process->GetThumbnailGenerator();
cell->SetThumbnail(
generator->GetThumbnailForRenderer(contents->render_view_host()));
}
cell->SchedulePaint();
} else {
// Need to figure out under what circumstances this is null and deal.
NOTIMPLEMENTED();
// Make sure we set the thumbnail, otherwise configured_thumbnail
// remains false and ConfigureNextUnconfiguredCell would get stuck.
if (show_thumbnails_)
cell->SetThumbnail(SkBitmap());
}
}
void TabOverviewController::DragStarted() {
DCHECK(!drag_browser_);
drag_browser_ = browser_;
static_cast<BrowserView*>(drag_browser_->window())->
browser_extender()->set_can_close(false);
}
void TabOverviewController::DragEnded() {
static_cast<BrowserView*>(drag_browser_->window())->
browser_extender()->set_can_close(true);
if (drag_browser_->tabstrip_model()->count() == 0)
drag_browser_->tabstrip_model()->delegate()->CloseFrameAfterDragSession();
drag_browser_ = NULL;
}
void TabOverviewController::MoveOffscreen() {
gfx::Rect bounds;
moved_offscreen_ = true;
host_->GetBounds(&bounds, true);
host_->SetBounds(gfx::Rect(-10000, -10000, bounds.width(), bounds.height()));
}
void TabOverviewController::SelectTab(int index) {
browser_->SelectTabContentsAt(index, true);
}
void TabOverviewController::FocusBrowser() {
static_cast<BrowserView*>(browser_->window())->GetWindow()->Activate();
}
void TabOverviewController::GridAnimationEnded() {
if (moved_offscreen_ || !change_window_bounds_on_animate_ || mutating_grid_)
return;
container_->SetBounds(target_bounds_);
grid_->UpdateDragController();
change_window_bounds_on_animate_ = false;
}
void TabOverviewController::GridAnimationProgressed() {
if (moved_offscreen_ || !change_window_bounds_on_animate_)
return;
DCHECK(!mutating_grid_);
// Schedule a paint before and after changing sizes to deal with the case
// of the view shrinking in size.
container_->SchedulePaint();
container_->SetBounds(
grid_->animation().CurrentValueBetween(start_bounds_, target_bounds_));
container_->SchedulePaint();
// Update the position of the dragged cell.
grid_->UpdateDragController();
}
void TabOverviewController::GridAnimationCanceled() {
change_window_bounds_on_animate_ = false;
}
void TabOverviewController::TabInsertedAt(TabContents* contents,
int index,
bool foreground) {
if (!grid_->modifying_model())
grid_->CancelDrag();
TabOverviewCell* child = new TabOverviewCell();
ConfigureCell(child, index);
mutating_grid_ = true;
grid_->InsertCell(index, child);
mutating_grid_ = false;
UpdateStartAndTargetBounds();
}
void TabOverviewController::TabClosingAt(TabContents* contents, int index) {
// Nothing to do, we only care when the tab is actually detached.
}
void TabOverviewController::TabDetachedAt(TabContents* contents, int index) {
if (!grid_->modifying_model())
grid_->CancelDrag();
scoped_ptr<TabOverviewCell> child(grid_->GetTabOverviewCellAt(index));
mutating_grid_ = true;
grid_->RemoveCell(index);
mutating_grid_ = false;
UpdateStartAndTargetBounds();
}
void TabOverviewController::TabMoved(TabContents* contents,
int from_index,
int to_index) {
if (!grid_->modifying_model())
grid_->CancelDrag();
mutating_grid_ = true;
grid_->MoveCell(from_index, to_index);
mutating_grid_ = false;
UpdateStartAndTargetBounds();
}
void TabOverviewController::TabChangedAt(TabContents* contents, int index,
TabChangeType change_type) {
ConfigureCell(grid_->GetTabOverviewCellAt(index), index);
}
void TabOverviewController::TabStripEmpty() {
if (!grid_->modifying_model()) {
grid_->CancelDrag();
// The tab strip is empty, hide the grid.
host_->Hide();
}
}
void TabOverviewController::ConfigureCell(TabOverviewCell* cell, int index) {
ConfigureCell(cell, model()->GetTabContentsAt(index));
}
void TabOverviewController::RecreateCells() {
grid_->RemoveAllChildViews(true);
if (model()) {
for (int i = 0; i < model()->count(); ++i) {
TabOverviewCell* child = new TabOverviewCell();
ConfigureCell(child, i);
grid_->AddChildView(child);
}
}
if (moved_offscreen_)
return;
if (grid()->GetChildViewCount() > 0) {
if (shown_)
host_->Show();
} else {
host_->Hide();
}
container_->SetBounds(CalculateContainerBounds());
}
void TabOverviewController::UpdateStartAndTargetBounds() {
if (moved_offscreen_ || !shown_)
return;
if (grid()->GetChildViewCount() == 0) {
host_->Hide();
} else {
start_bounds_ = container_->bounds();
target_bounds_ = CalculateContainerBounds();
change_window_bounds_on_animate_ = (start_bounds_ != target_bounds_);
}
}
gfx::Rect TabOverviewController::CalculateContainerBounds() {
gfx::Rect host_bounds = CalculateHostBounds();
const gfx::Size& host_size = CalculateHostBounds().size();
gfx::Size pref = container_->GetPreferredSize();
int relative_horizontal_center = horizontal_center_ - host_bounds.x();
int x = relative_horizontal_center - pref.width() / 2;
int y = host_size.height() - pref.height();
return gfx::Rect(x, y, pref.width(), pref.height()).
AdjustToFit(gfx::Rect(0, 0, host_size.width(), host_size.height()));
}
gfx::Rect TabOverviewController::CalculateHostBounds() {
int max_width = monitor_bounds_.width() - kMonitorPadding * 2;
int window_height = monitor_bounds_.height() * kWindowHeight;
int max_height = static_cast<int>(monitor_bounds_.height() *
kOverviewHeight);
return gfx::Rect(monitor_bounds_.x() + kMonitorPadding,
monitor_bounds_.bottom() - window_height -
kWindowToOverviewPadding - max_height, max_width,
max_height);
}
void TabOverviewController::StartConfiguring() {
show_thumbnails_ = true;
configure_timer_.Stop();
configure_timer_.Start(
base::TimeDelta::FromMilliseconds(10), this,
&TabOverviewController::ConfigureNextUnconfiguredCell);
}
void TabOverviewController::ConfigureNextUnconfiguredCell() {
for (int i = 0; i < grid_->GetChildViewCount(); ++i) {
TabOverviewCell* cell = grid_->GetTabOverviewCellAt(i);
if (!cell->configured_thumbnail()) {
ConfigureCell(cell, i);
return;
}
}
configure_timer_.Stop();
}
void TabOverviewController::StartDelayTimer() {
configure_timer_.Stop();
delay_timer_.Stop();
delay_timer_.Start(
base::TimeDelta::FromMilliseconds(350), this,
&TabOverviewController::StartConfiguring);
}