// Copyright (c) 2011 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/ui/gtk/infobars/infobar_container_gtk.h" #include <gtk/gtk.h> #include <utility> #include "base/command_line.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/tab_contents/infobar_delegate.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/gtk/browser_window_gtk.h" #include "chrome/browser/ui/gtk/gtk_theme_service.h" #include "chrome/browser/ui/gtk/gtk_util.h" #include "chrome/browser/ui/gtk/infobars/infobar_gtk.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/common/notification_details.h" #include "content/common/notification_source.h" #include "third_party/skia/include/core/SkPaint.h" namespace { static const char* kInfoBar = "info-bar"; // If |infobar_widget| matches |info_bar_delegate|, then close the infobar. void AnimateClosingForDelegate(GtkWidget* infobar_widget, gpointer info_bar_delegate) { InfoBarDelegate* delegate = static_cast<InfoBarDelegate*>(info_bar_delegate); InfoBar* infobar = reinterpret_cast<InfoBar*>( g_object_get_data(G_OBJECT(infobar_widget), kInfoBar)); if (!infobar) { NOTREACHED(); return; } if (delegate == infobar->delegate()) infobar->AnimateClose(); } // If |infobar_widget| matches |info_bar_delegate|, then close the infobar w/o // an animation. void ClosingForDelegate(GtkWidget* infobar_widget, gpointer info_bar_delegate) { InfoBarDelegate* delegate = static_cast<InfoBarDelegate*>(info_bar_delegate); InfoBar* infobar = reinterpret_cast<InfoBar*>( g_object_get_data(G_OBJECT(infobar_widget), kInfoBar)); if (!infobar) { NOTREACHED(); return; } if (delegate == infobar->delegate()) infobar->Close(); } // Get the height of the widget and add it to |userdata|, but only if it is in // the process of animating. void SumAnimatingBarHeight(GtkWidget* widget, gpointer userdata) { int* height_sum = static_cast<int*>(userdata); InfoBar* infobar = reinterpret_cast<InfoBar*>( g_object_get_data(G_OBJECT(widget), kInfoBar)); if (infobar->IsAnimating()) *height_sum += widget->allocation.height; } } // namespace // InfoBarContainerGtk, public: ------------------------------------------------ InfoBarContainerGtk::InfoBarContainerGtk(Profile* profile) : profile_(profile), tab_contents_(NULL), container_(gtk_vbox_new(FALSE, 0)) { gtk_widget_show(widget()); } InfoBarContainerGtk::~InfoBarContainerGtk() { ChangeTabContents(NULL); container_.Destroy(); } void InfoBarContainerGtk::ChangeTabContents(TabContents* contents) { if (tab_contents_) registrar_.RemoveAll(); gtk_util::RemoveAllChildren(widget()); UpdateToolbarInfoBarState(NULL, false); tab_contents_ = contents; if (tab_contents_) { UpdateInfoBars(); Source<TabContents> source(tab_contents_); registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_ADDED, source); registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, source); registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REPLACED, source); } } void InfoBarContainerGtk::RemoveDelegate(InfoBarDelegate* delegate) { tab_contents_->RemoveInfoBar(delegate); } int InfoBarContainerGtk::TotalHeightOfAnimatingBars() const { int sum = 0; gtk_container_foreach(GTK_CONTAINER(widget()), SumAnimatingBarHeight, &sum); return sum; } // InfoBarContainerGtk, NotificationObserver implementation: ------------------- void InfoBarContainerGtk::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { if (type == NotificationType::TAB_CONTENTS_INFOBAR_ADDED) { AddInfoBar(Details<InfoBarDelegate>(details).ptr(), true); } else if (type == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED) { RemoveInfoBar(Details<InfoBarDelegate>(details).ptr(), true); } else if (type == NotificationType::TAB_CONTENTS_INFOBAR_REPLACED) { std::pair<InfoBarDelegate*, InfoBarDelegate*>* delegates = Details<std::pair<InfoBarDelegate*, InfoBarDelegate*> >(details).ptr(); // By not animating the removal/addition, this appears to be a replace. RemoveInfoBar(delegates->first, false); AddInfoBar(delegates->second, false); } else { NOTREACHED(); } } // InfoBarContainerGtk, private: ----------------------------------------------- void InfoBarContainerGtk::UpdateInfoBars() { for (size_t i = 0; i < tab_contents_->infobar_count(); ++i) { InfoBarDelegate* delegate = tab_contents_->GetInfoBarDelegateAt(i); AddInfoBar(delegate, false); } } void InfoBarContainerGtk::ShowArrowForDelegate(InfoBarDelegate* delegate, bool animate) { GList* children = gtk_container_get_children(GTK_CONTAINER(widget())); if (!children) return; // Iterate through the infobars and find the last one that isn't closing. InfoBar* last_bar = NULL; InfoBar* this_bar = NULL; for (GList* iter = children; iter != NULL; iter = iter->next) { this_bar = reinterpret_cast<InfoBar*>( g_object_get_data(G_OBJECT(iter->data), kInfoBar)); if (this_bar->delegate() == delegate) break; if (!this_bar->IsClosing()) last_bar = this_bar; this_bar = NULL; } if (last_bar) last_bar->ShowArrowFor(this_bar, animate); else UpdateToolbarInfoBarState(this_bar, animate); g_list_free(children); } void InfoBarContainerGtk::AddInfoBar(InfoBarDelegate* delegate, bool animate) { InfoBar* infobar = delegate->CreateInfoBar(); infobar->set_container(this); infobar->SetThemeProvider(GtkThemeService::GetFrom(profile_)); gtk_box_pack_start(GTK_BOX(widget()), infobar->widget(), FALSE, FALSE, 0); if (animate) infobar->AnimateOpen(); else infobar->Open(); ShowArrowForDelegate(delegate, animate); } void InfoBarContainerGtk::RemoveInfoBar(InfoBarDelegate* delegate, bool animate) { if (animate) { gtk_container_foreach(GTK_CONTAINER(widget()), AnimateClosingForDelegate, delegate); } else { gtk_container_foreach(GTK_CONTAINER(widget()), ClosingForDelegate, delegate); } InfoBarDelegate* next_delegate = NULL; for (size_t i = 1; i < tab_contents_->infobar_count(); ++i) { if (tab_contents_->GetInfoBarDelegateAt(i - 1) == delegate) { next_delegate = tab_contents_->GetInfoBarDelegateAt(i); break; } } ShowArrowForDelegate(next_delegate, animate); } void InfoBarContainerGtk::UpdateToolbarInfoBarState(InfoBar* infobar, bool animate) { GtkWindow* parent = platform_util::GetTopLevel(widget()); BrowserWindowGtk* browser_window = BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent); if (browser_window) browser_window->SetInfoBarShowing(infobar, animate); }