// 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" // 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 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(drag_browser_->window())-> browser_extender()->set_can_close(false); } void TabOverviewController::DragEnded() { static_cast(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() { TabOverviewTypes::Message message; message.set_type(TabOverviewTypes::Message::WM_FOCUS_WINDOW); GtkWidget* browser_widget = GTK_WIDGET(browser_->window()->GetNativeHandle()); message.set_param(0, x11_util::GetX11WindowFromGtkWidget(browser_widget)); TabOverviewTypes::instance()->SendMessage(message); } 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 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(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); }