| Index: chrome/browser/chromeos/wm_overview_controller.cc
|
| ===================================================================
|
| --- chrome/browser/chromeos/wm_overview_controller.cc (revision 0)
|
| +++ chrome/browser/chromeos/wm_overview_controller.cc (revision 0)
|
| @@ -0,0 +1,517 @@
|
| +// Copyright (c) 2010 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/chromeos/wm_overview_controller.h"
|
| +
|
| +#include <algorithm>
|
| +#include <vector>
|
| +
|
| +#include "base/linked_ptr.h"
|
| +#include "chrome/browser/browser.h"
|
| +#include "chrome/browser/browser_process.h"
|
| +#include "chrome/browser/chromeos/wm_ipc.h"
|
| +#include "chrome/browser/chromeos/wm_overview_snapshot.h"
|
| +#include "chrome/browser/renderer_host/render_view_host.h"
|
| +#include "chrome/browser/tab_contents/tab_contents.h"
|
| +#include "chrome/browser/tab_contents/tab_contents_view.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/common/notification_service.h"
|
| +#include "views/widget/root_view.h"
|
| +#include "views/widget/widget_gtk.h"
|
| +#include "views/window/window.h"
|
| +
|
| +using std::vector;
|
| +
|
| +#if !defined(OS_CHROMEOS)
|
| +#error This file is only meant to be compiled for ChromeOS
|
| +#endif
|
| +
|
| +namespace chromeos {
|
| +
|
| +class BrowserListener : public TabStripModelObserver {
|
| + public:
|
| + BrowserListener(Browser* browser, WmOverviewController* parent);
|
| + ~BrowserListener();
|
| +
|
| + // Begin TabStripModelObserver methods
|
| + virtual void TabInsertedAt(TabContents* contents,
|
| + int index,
|
| + bool foreground);
|
| + virtual void TabClosingAt(TabContents* contents, int index) {}
|
| + virtual void TabDetachedAt(TabContents* contents, int index);
|
| + virtual void TabMoved(TabContents* contents,
|
| + int from_index,
|
| + int to_index);
|
| + virtual void TabChangedAt(TabContents* contents, int index,
|
| + TabStripModelObserver::TabChangeType change_type);
|
| + virtual void TabStripEmpty();
|
| + virtual void TabDeselectedAt(TabContents* contents, int index) {}
|
| + virtual void TabSelectedAt(TabContents* old_contents,
|
| + TabContents* new_contents,
|
| + int index,
|
| + bool user_gesture);
|
| + // End TabStripModelObserver methods
|
| +
|
| + // Returns the number of tabs in this child.
|
| + int count() const { return browser_->tabstrip_model()->count(); }
|
| +
|
| + // Returns the browser that this child gets its data from.
|
| + Browser* browser() const { return browser_; }
|
| +
|
| + // Removes all the snapshots and re-populates them from the browser.
|
| + void RecreateSnapshots();
|
| +
|
| + // Updates the selected index and tab count on the toplevel window.
|
| + void UpdateSelectedIndex(int index);
|
| +
|
| + // Finds the first cell with no snapshot and invokes ConfigureCell
|
| + // for it. Returns false if there are no more cells to configure on
|
| + // this listener.
|
| + bool ConfigureNextUnconfiguredSnapshot();
|
| +
|
| + // Saves the currently selected tab.
|
| + void SaveCurrentTab() { original_selected_tab_ = browser_->selected_index(); }
|
| +
|
| + // Reverts the selected browser tab to the tab that was selected
|
| + // when This BrowserListener was created, or the last time
|
| + // SaveCurrentTab was called.
|
| + void RestoreOriginalSelectedTab();
|
| +
|
| + // Selects the tab at the given index.
|
| + void SelectTab(int index);
|
| +
|
| + // Shows any snapshots that are not visible, and updates their
|
| + // bitmaps.
|
| + void ShowSnapshots();
|
| +
|
| + private:
|
| + // Returns the tab contents from the tab model for this child at index.
|
| + TabContents* GetTabContentsAt(int index) const {
|
| + return browser_->tabstrip_model()->GetTabContentsAt(index);
|
| + }
|
| +
|
| + // Configures a cell from the tab contents.
|
| + void ConfigureCell(WmOverviewSnapshot* cell, TabContents* contents);
|
| +
|
| + // Configures a cell from the model.
|
| + void ConfigureCell(WmOverviewSnapshot* cell, int index) {
|
| + ConfigureCell(cell, GetTabContentsAt(index));
|
| + }
|
| +
|
| + // Inserts a new snapshot, initialized from the model, at the given
|
| + // index, and renumbers any following snapshots.
|
| + void InsertSnapshot(int index);
|
| +
|
| + // Removes the snapshot at index.
|
| + void ClearSnapshot(int index);
|
| +
|
| + // Renumbers the index atom in the snapshots starting at the given
|
| + // index.
|
| + void RenumberSnapshots(int start_index);
|
| +
|
| + Browser* browser_; // Not owned
|
| + WmOverviewController* controller_; // Not owned
|
| +
|
| + // Widgets containing snapshot images for this browser.
|
| + typedef std::vector<WmOverviewSnapshot* > SnapshotVector;
|
| + SnapshotVector snapshots_;
|
| +
|
| + // True if the snapshots are showing.
|
| + bool snapshots_showing_;
|
| +
|
| + // The tab selected the last time SaveCurrentTab is called.
|
| + int original_selected_tab_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(BrowserListener);
|
| +};
|
| +
|
| +BrowserListener::BrowserListener(Browser* browser,
|
| + WmOverviewController* controller)
|
| + : browser_(browser),
|
| + controller_(controller),
|
| + snapshots_showing_(false),
|
| + original_selected_tab_(-1) {
|
| + CHECK(browser_);
|
| + CHECK(controller_);
|
| + browser_->tabstrip_model()->AddObserver(this);
|
| +}
|
| +
|
| +BrowserListener::~BrowserListener() {
|
| + browser_->tabstrip_model()->RemoveObserver(this);
|
| +}
|
| +
|
| +void BrowserListener::TabInsertedAt(TabContents* contents,
|
| + int index,
|
| + bool foreground) {
|
| + InsertSnapshot(index);
|
| + RenumberSnapshots(index);
|
| + UpdateSelectedIndex(browser_->selected_index());
|
| +}
|
| +
|
| +void BrowserListener::TabDetachedAt(TabContents* contents, int index) {
|
| + ClearSnapshot(index);
|
| + UpdateSelectedIndex(browser_->selected_index());
|
| + RenumberSnapshots(index);
|
| +}
|
| +
|
| +void BrowserListener::TabMoved(TabContents* contents,
|
| + int from_index,
|
| + int to_index) {
|
| + // Need to reorder tab in the snapshots list, and reset the window
|
| + // type atom on the affected snapshots (the one moved, and all the
|
| + // ones after it), so that their indices are correct.
|
| + WmOverviewSnapshot* snapshot = snapshots_[from_index];
|
| + snapshots_.erase(snapshots_.begin() + from_index);
|
| + snapshots_.insert(snapshots_.begin() + to_index, snapshot);
|
| +
|
| + RenumberSnapshots(std::min(to_index, from_index));
|
| +}
|
| +
|
| +void BrowserListener::TabChangedAt(
|
| + TabContents* contents,
|
| + int index,
|
| + TabStripModelObserver::TabChangeType change_type) {
|
| + snapshots_[index]->reload_snapshot();
|
| + controller_->StartDelayTimer();
|
| +}
|
| +
|
| +void BrowserListener::TabStripEmpty() {
|
| + snapshots_.clear();
|
| +}
|
| +
|
| +void BrowserListener::TabSelectedAt(TabContents* old_contents,
|
| + TabContents* new_contents,
|
| + int index,
|
| + bool user_gesture) {
|
| + UpdateSelectedIndex(index);
|
| +}
|
| +
|
| +void BrowserListener::RecreateSnapshots() {
|
| + snapshots_.clear();
|
| +
|
| + for (int i = 0; i < count(); ++i) {
|
| + InsertSnapshot(i);
|
| + }
|
| +
|
| + RenumberSnapshots(0);
|
| +}
|
| +
|
| +void BrowserListener::UpdateSelectedIndex(int index) {
|
| + // Get the window params and check to make sure that they are
|
| + // different from what we know before we set them, to avoid extra
|
| + // notifications.
|
| + std::vector<int> params;
|
| + WmIpc::WindowType type = WmIpc::instance()->GetWindowType(
|
| + GTK_WIDGET(browser_->window()->GetNativeHandle()),
|
| + ¶ms);
|
| + DCHECK(type == WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL);
|
| + if (params.size() > 1) {
|
| + if (params[0] == browser_->tab_count() &&
|
| + params[0] == index)
|
| + return;
|
| + }
|
| +
|
| + params.clear();
|
| + params.push_back(browser_->tab_count());
|
| + params.push_back(index);
|
| + WmIpc::instance()->SetWindowType(
|
| + GTK_WIDGET(browser_->window()->GetNativeHandle()),
|
| + WmIpc::WINDOW_TYPE_CHROME_TOPLEVEL,
|
| + ¶ms);
|
| +}
|
| +
|
| +bool BrowserListener::ConfigureNextUnconfiguredSnapshot() {
|
| + for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) {
|
| + WmOverviewSnapshot* cell = snapshots_[i];
|
| + if (!cell->configured_snapshot()) {
|
| + ConfigureCell(cell, GetTabContentsAt(i));
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +void BrowserListener::RestoreOriginalSelectedTab() {
|
| + if (original_selected_tab_ >= 0) {
|
| + browser_->SelectTabContentsAt(original_selected_tab_, false);
|
| + }
|
| +}
|
| +
|
| +void BrowserListener::ShowSnapshots() {
|
| + for (SnapshotVector::size_type i = 0; i < snapshots_.size(); ++i) {
|
| + WmOverviewSnapshot* snapshot = snapshots_[i];
|
| + snapshot->reload_snapshot();
|
| + if (!snapshot->IsVisible()) {
|
| + snapshot->Show();
|
| + }
|
| + }
|
| +}
|
| +
|
| +void BrowserListener::SelectTab(int index) {
|
| + browser_->SelectTabContentsAt(index, true);
|
| +}
|
| +
|
| +void BrowserListener::ConfigureCell(WmOverviewSnapshot* cell,
|
| + TabContents* contents) {
|
| + if (contents) {
|
| + if (controller_->allow_show_snapshots()) {
|
| + ThumbnailGenerator* generator =
|
| + g_browser_process->GetThumbnailGenerator();
|
| + // TODO: Make sure that if the cell gets deleted before the
|
| + // callback is called that it sticks around until it gets
|
| + // called. (some kind of "in flight" list that uses linked_ptr
|
| + // to make sure they don't actually get deleted?) Also, make
|
| + // sure that any request for a thumbnail eventually returns
|
| + // (even if it has bogus data), so we don't leak orphaned cells.
|
| + ThumbnailGenerator::ThumbnailReadyCallback* callback =
|
| + NewCallback(cell, &WmOverviewSnapshot::SetImage);
|
| + generator->AskForThumbnail(contents->render_view_host(),
|
| + callback, cell->size());
|
| + }
|
| + } else {
|
| + // This happens because the contents haven't been loaded yet.
|
| +
|
| + // Make sure we set the snapshot image to something, otherwise
|
| + // configured_snapshot remains false and ConfigureNextUnconfiguredSnapshot
|
| + // would get stuck.
|
| + if (controller_->allow_show_snapshots()) {
|
| + cell->SetImage(SkBitmap());
|
| + }
|
| + }
|
| +}
|
| +
|
| +void BrowserListener::InsertSnapshot(int index) {
|
| + WmOverviewSnapshot* snapshot = new WmOverviewSnapshot;
|
| + gfx::Rect bounds =
|
| + static_cast<BrowserView*>(browser_->window())->GetClientAreaBounds();
|
| + gfx::Size size(bounds.width()/2, bounds.height()/2);
|
| + snapshot->Init(size, browser_, index);
|
| + snapshots_.insert(snapshots_.begin() + index, snapshot);
|
| + snapshot->reload_snapshot();
|
| + controller_->StartDelayTimer();
|
| +}
|
| +
|
| +// Removes the snapshot at index.
|
| +void BrowserListener::ClearSnapshot(int index) {
|
| + snapshots_[index]->CloseNow();
|
| + snapshots_.erase(snapshots_.begin() + index);
|
| +}
|
| +
|
| +void BrowserListener::RenumberSnapshots(int start_index) {
|
| + int changes = 0;
|
| + for (SnapshotVector::size_type i = start_index; i < snapshots_.size(); ++i) {
|
| + if (snapshots_[i]->index() != static_cast<int>(i)) {
|
| + snapshots_[i]->UpdateIndex(browser_, i);
|
| + changes++;
|
| + }
|
| + }
|
| +}
|
| +
|
| +///////////////////////////////////
|
| +// WmOverviewController methods
|
| +
|
| +// static
|
| +WmOverviewController* WmOverviewController::instance() {
|
| + static WmOverviewController* instance = NULL;
|
| + if (!instance) {
|
| + instance = Singleton<WmOverviewController>::get();
|
| + }
|
| + return instance;
|
| +}
|
| +
|
| +WmOverviewController::WmOverviewController()
|
| + : allow_show_snapshots_(false) {
|
| + AddAllBrowsers();
|
| + BrowserList::AddObserver(this);
|
| + WmMessageListener::instance()->AddObserver(this);
|
| +}
|
| +
|
| +WmOverviewController::~WmOverviewController() {
|
| + WmMessageListener::instance()->RemoveObserver(this);
|
| + BrowserList::RemoveObserver(this);
|
| + listeners_.clear();
|
| +}
|
| +
|
| +void WmOverviewController::Observe(NotificationType type,
|
| + const NotificationSource& source,
|
| + const NotificationDetails& details) {
|
| + // Now that the browser window is ready, we create the snapshots.
|
| + if (type == NotificationType::BROWSER_WINDOW_READY) {
|
| + const Browser* browser = Source<const Browser>(source).ptr();
|
| + BrowserListenerVector::value_type new_listener(
|
| + new BrowserListener(const_cast<Browser*>(browser), this));
|
| + listeners_.push_back(new_listener);
|
| + new_listener->RecreateSnapshots();
|
| +
|
| + // This makes sure that the new listener is in the right order (to
|
| + // match the order in the browser list).
|
| + AddAllBrowsers();
|
| + }
|
| +}
|
| +
|
| +// Called immediately before a browser is removed from the list.
|
| +void WmOverviewController::OnBrowserRemoving(const Browser* browser) {
|
| + for (BrowserListenerVector::iterator i = listeners_.begin();
|
| + i != listeners_.end(); ++i) {
|
| + if ((*i)->browser() == browser) {
|
| + listeners_.erase(i);
|
| + return;
|
| + }
|
| + }
|
| +}
|
| +
|
| +BrowserView* GetBrowserViewForGdkWindow(GdkWindow* gdk_window) {
|
| + gpointer data = NULL;
|
| + gdk_window_get_user_data(gdk_window, &data);
|
| + GtkWidget* widget = reinterpret_cast<GtkWidget*>(data);
|
| + if (widget) {
|
| + GtkWindow* gtk_window = GTK_WINDOW(widget);
|
| + return BrowserView::GetBrowserViewForNativeWindow(gtk_window);
|
| + } else {
|
| + return NULL;
|
| + }
|
| +}
|
| +
|
| +void WmOverviewController::ProcessWmMessage(const WmIpc::Message& message,
|
| + GdkWindow* window) {
|
| + switch (message.type()) {
|
| + case WmIpc::Message::CHROME_NOTIFY_LAYOUT_MODE: {
|
| + if (message.param(0) == 0 || BrowserList::size() == 0) {
|
| + Hide(message.param(1) != 0);
|
| + } else {
|
| + Show();
|
| + }
|
| + break;
|
| + }
|
| + case WmIpc::Message::CHROME_NOTIFY_TAB_SELECT: {
|
| + BrowserView* browser_window = GetBrowserViewForGdkWindow(window);
|
| + // Find out which listener this goes to, and send it there.
|
| + for (BrowserListenerVector::iterator i = listeners_.begin();
|
| + i != listeners_.end(); ++i) {
|
| + if ((*i)->browser()->window() == browser_window) {
|
| + (*i)->SelectTab(message.param(0));
|
| + break;
|
| + }
|
| + }
|
| + break;
|
| + }
|
| + default:
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void WmOverviewController::StartDelayTimer() {
|
| + // We leave the delay timer running if it already is -- this means
|
| + // we're rate limiting the number of times we can reconfigure the
|
| + // snapshots (at most once every 350ms). If we were to restart the
|
| + // delay timer, it could result in a very long delay until they get
|
| + // configured if tabs keep changing.
|
| + if (!delay_timer_.IsRunning()) {
|
| + configure_timer_.Stop();
|
| + // Note that this pause is kind of a hack: it's just long enough
|
| + // that the overview-mode transitions will have finished happening
|
| + // before we start refreshing snapshots, so we don't bog the CPU
|
| + // while it's trying to animate the overview transitions.
|
| + delay_timer_.Start(
|
| + base::TimeDelta::FromMilliseconds(350), this,
|
| + &WmOverviewController::StartConfiguring);
|
| + }
|
| +}
|
| +
|
| +void WmOverviewController::RestoreTabSelections() {
|
| + for (BrowserListenerVector::iterator i = listeners_.begin();
|
| + i != listeners_.end(); ++i) {
|
| + (*i)->RestoreOriginalSelectedTab();
|
| + }
|
| +}
|
| +
|
| +void WmOverviewController::SaveTabSelections() {
|
| + for (BrowserListenerVector::iterator i = listeners_.begin();
|
| + i != listeners_.end(); ++i) {
|
| + (*i)->SaveCurrentTab();
|
| + }
|
| +}
|
| +
|
| +void WmOverviewController::Show() {
|
| + SaveTabSelections();
|
| + for (BrowserListenerVector::iterator i = listeners_.begin();
|
| + i != listeners_.end(); ++i) {
|
| + (*i)->ShowSnapshots();
|
| + }
|
| + allow_show_snapshots_ = false;
|
| + StartDelayTimer();
|
| +}
|
| +
|
| +void WmOverviewController::Hide(bool cancelled) {
|
| + configure_timer_.Stop();
|
| + delay_timer_.Stop();
|
| + if (cancelled) {
|
| + RestoreTabSelections();
|
| + }
|
| +}
|
| +
|
| +void WmOverviewController::StartConfiguring() {
|
| + allow_show_snapshots_ = true;
|
| + configure_timer_.Stop();
|
| + configure_timer_.Start(
|
| + base::TimeDelta::FromMilliseconds(10), this,
|
| + &WmOverviewController::ConfigureNextUnconfiguredSnapshot);
|
| +}
|
| +
|
| +// Just configure one unconfigured cell. If there aren't any left,
|
| +// then stop the timer.
|
| +void WmOverviewController::ConfigureNextUnconfiguredSnapshot() {
|
| + for (BrowserListenerVector::size_type i = 0; i < listeners_.size(); ++i) {
|
| + BrowserListener* listener = listeners_[i].get();
|
| + if (listener->ConfigureNextUnconfiguredSnapshot())
|
| + return;
|
| + }
|
| + configure_timer_.Stop();
|
| +}
|
| +
|
| +void WmOverviewController::AddAllBrowsers() {
|
| + // Make a copy so the old ones aren't deleted yet.
|
| + BrowserListenerVector old_listeners;
|
| +
|
| + listeners_.swap(old_listeners);
|
| +
|
| + // Iterator through the browser list, adding all the browsers in the
|
| + // new order. If they were in the old list of listeners, just copy
|
| + // that linked pointer, instead of making a new listener, so that we
|
| + // can avoid lots of spurious destroy/create messages.
|
| + BrowserList::const_iterator iterator = BrowserList::begin();
|
| + while (iterator != BrowserList::end()) {
|
| + BrowserListenerVector::value_type item(
|
| + BrowserListenerVector::value_type(NULL));
|
| + for (BrowserListenerVector::iterator old_iter = old_listeners.begin();
|
| + old_iter != old_listeners.end(); ++old_iter) {
|
| + if ((*old_iter)->browser() == *iterator) {
|
| + item = *old_iter;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // This browser isn't owned by any listener, so create it.
|
| + if (item.get() == NULL) {
|
| + item = BrowserListenerVector::value_type(
|
| + new BrowserListener(*iterator, this));
|
| +
|
| + // This browser didn't already exist, and so we haven't been
|
| + // watching it for tab insertions, so we need to create the
|
| + // snapshots associated with it.
|
| + item->RecreateSnapshots();
|
| + }
|
| + listeners_.push_back(item);
|
| + ++iterator;
|
| + }
|
| +
|
| + // Make sure we get notifications for when browser windows are ready.
|
| + if (registrar_.IsEmpty())
|
| + registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY,
|
| + NotificationService::AllSources());
|
| +}
|
| +
|
| +} // namespace chromeos
|
|
|
| Property changes on: chrome/browser/chromeos/wm_overview_controller.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|