Home | History | Annotate | Download | only in infobars
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/gtk/infobars/infobar_container_gtk.h"
      6 
      7 #include <gtk/gtk.h>
      8 
      9 #include <utility>
     10 
     11 #include "base/command_line.h"
     12 #include "chrome/browser/platform_util.h"
     13 #include "chrome/browser/tab_contents/infobar_delegate.h"
     14 #include "chrome/browser/ui/browser_window.h"
     15 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
     16 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     17 #include "chrome/browser/ui/gtk/gtk_util.h"
     18 #include "chrome/browser/ui/gtk/infobars/infobar_gtk.h"
     19 #include "content/browser/tab_contents/tab_contents.h"
     20 #include "content/common/notification_details.h"
     21 #include "content/common/notification_source.h"
     22 #include "third_party/skia/include/core/SkPaint.h"
     23 
     24 namespace {
     25 
     26 static const char* kInfoBar = "info-bar";
     27 
     28 // If |infobar_widget| matches |info_bar_delegate|, then close the infobar.
     29 void AnimateClosingForDelegate(GtkWidget* infobar_widget,
     30                                gpointer info_bar_delegate) {
     31   InfoBarDelegate* delegate =
     32       static_cast<InfoBarDelegate*>(info_bar_delegate);
     33   InfoBar* infobar = reinterpret_cast<InfoBar*>(
     34       g_object_get_data(G_OBJECT(infobar_widget), kInfoBar));
     35 
     36   if (!infobar) {
     37     NOTREACHED();
     38     return;
     39   }
     40 
     41   if (delegate == infobar->delegate())
     42     infobar->AnimateClose();
     43 }
     44 
     45 // If |infobar_widget| matches |info_bar_delegate|, then close the infobar w/o
     46 // an animation.
     47 void ClosingForDelegate(GtkWidget* infobar_widget, gpointer info_bar_delegate) {
     48   InfoBarDelegate* delegate =
     49       static_cast<InfoBarDelegate*>(info_bar_delegate);
     50   InfoBar* infobar = reinterpret_cast<InfoBar*>(
     51       g_object_get_data(G_OBJECT(infobar_widget), kInfoBar));
     52 
     53   if (!infobar) {
     54     NOTREACHED();
     55     return;
     56   }
     57 
     58   if (delegate == infobar->delegate())
     59     infobar->Close();
     60 }
     61 
     62 // Get the height of the widget and add it to |userdata|, but only if it is in
     63 // the process of animating.
     64 void SumAnimatingBarHeight(GtkWidget* widget, gpointer userdata) {
     65   int* height_sum = static_cast<int*>(userdata);
     66   InfoBar* infobar = reinterpret_cast<InfoBar*>(
     67       g_object_get_data(G_OBJECT(widget), kInfoBar));
     68   if (infobar->IsAnimating())
     69     *height_sum += widget->allocation.height;
     70 }
     71 
     72 }  // namespace
     73 
     74 // InfoBarContainerGtk, public: ------------------------------------------------
     75 
     76 InfoBarContainerGtk::InfoBarContainerGtk(Profile* profile)
     77     : profile_(profile),
     78       tab_contents_(NULL),
     79       container_(gtk_vbox_new(FALSE, 0)) {
     80   gtk_widget_show(widget());
     81 }
     82 
     83 InfoBarContainerGtk::~InfoBarContainerGtk() {
     84   ChangeTabContents(NULL);
     85 
     86   container_.Destroy();
     87 }
     88 
     89 void InfoBarContainerGtk::ChangeTabContents(TabContents* contents) {
     90   if (tab_contents_)
     91     registrar_.RemoveAll();
     92 
     93   gtk_util::RemoveAllChildren(widget());
     94   UpdateToolbarInfoBarState(NULL, false);
     95 
     96   tab_contents_ = contents;
     97   if (tab_contents_) {
     98     UpdateInfoBars();
     99     Source<TabContents> source(tab_contents_);
    100     registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_ADDED, source);
    101     registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED,
    102                    source);
    103     registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REPLACED,
    104                    source);
    105   }
    106 }
    107 
    108 void InfoBarContainerGtk::RemoveDelegate(InfoBarDelegate* delegate) {
    109   tab_contents_->RemoveInfoBar(delegate);
    110 }
    111 
    112 int InfoBarContainerGtk::TotalHeightOfAnimatingBars() const {
    113   int sum = 0;
    114   gtk_container_foreach(GTK_CONTAINER(widget()), SumAnimatingBarHeight, &sum);
    115   return sum;
    116 }
    117 
    118 // InfoBarContainerGtk, NotificationObserver implementation: -------------------
    119 
    120 void InfoBarContainerGtk::Observe(NotificationType type,
    121                                   const NotificationSource& source,
    122                                   const NotificationDetails& details) {
    123   if (type == NotificationType::TAB_CONTENTS_INFOBAR_ADDED) {
    124     AddInfoBar(Details<InfoBarDelegate>(details).ptr(), true);
    125   } else if (type == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED) {
    126     RemoveInfoBar(Details<InfoBarDelegate>(details).ptr(), true);
    127   } else if (type == NotificationType::TAB_CONTENTS_INFOBAR_REPLACED) {
    128     std::pair<InfoBarDelegate*, InfoBarDelegate*>* delegates =
    129         Details<std::pair<InfoBarDelegate*, InfoBarDelegate*> >(details).ptr();
    130 
    131     // By not animating the removal/addition, this appears to be a replace.
    132     RemoveInfoBar(delegates->first, false);
    133     AddInfoBar(delegates->second, false);
    134   } else {
    135     NOTREACHED();
    136   }
    137 }
    138 
    139 // InfoBarContainerGtk, private: -----------------------------------------------
    140 
    141 void InfoBarContainerGtk::UpdateInfoBars() {
    142   for (size_t i = 0; i < tab_contents_->infobar_count(); ++i) {
    143     InfoBarDelegate* delegate = tab_contents_->GetInfoBarDelegateAt(i);
    144     AddInfoBar(delegate, false);
    145   }
    146 }
    147 
    148 void InfoBarContainerGtk::ShowArrowForDelegate(InfoBarDelegate* delegate,
    149                                                bool animate) {
    150   GList* children = gtk_container_get_children(GTK_CONTAINER(widget()));
    151   if (!children)
    152     return;
    153 
    154   // Iterate through the infobars and find the last one that isn't closing.
    155   InfoBar* last_bar = NULL;
    156   InfoBar* this_bar = NULL;
    157   for (GList* iter = children; iter != NULL; iter = iter->next) {
    158     this_bar = reinterpret_cast<InfoBar*>(
    159         g_object_get_data(G_OBJECT(iter->data), kInfoBar));
    160 
    161     if (this_bar->delegate() == delegate)
    162       break;
    163 
    164     if (!this_bar->IsClosing())
    165       last_bar = this_bar;
    166 
    167     this_bar = NULL;
    168   }
    169 
    170   if (last_bar)
    171     last_bar->ShowArrowFor(this_bar, animate);
    172   else
    173     UpdateToolbarInfoBarState(this_bar, animate);
    174 
    175   g_list_free(children);
    176 }
    177 
    178 void InfoBarContainerGtk::AddInfoBar(InfoBarDelegate* delegate, bool animate) {
    179   InfoBar* infobar = delegate->CreateInfoBar();
    180   infobar->set_container(this);
    181   infobar->SetThemeProvider(GtkThemeService::GetFrom(profile_));
    182   gtk_box_pack_start(GTK_BOX(widget()), infobar->widget(),
    183                      FALSE, FALSE, 0);
    184 
    185   if (animate)
    186     infobar->AnimateOpen();
    187   else
    188     infobar->Open();
    189 
    190   ShowArrowForDelegate(delegate, animate);
    191 }
    192 
    193 void InfoBarContainerGtk::RemoveInfoBar(InfoBarDelegate* delegate,
    194                                         bool animate) {
    195   if (animate) {
    196     gtk_container_foreach(GTK_CONTAINER(widget()),
    197                           AnimateClosingForDelegate, delegate);
    198   } else {
    199     gtk_container_foreach(GTK_CONTAINER(widget()), ClosingForDelegate,
    200                           delegate);
    201   }
    202 
    203   InfoBarDelegate* next_delegate = NULL;
    204   for (size_t i = 1; i < tab_contents_->infobar_count(); ++i) {
    205     if (tab_contents_->GetInfoBarDelegateAt(i - 1) == delegate) {
    206       next_delegate = tab_contents_->GetInfoBarDelegateAt(i);
    207       break;
    208     }
    209   }
    210 
    211   ShowArrowForDelegate(next_delegate, animate);
    212 }
    213 
    214 void InfoBarContainerGtk::UpdateToolbarInfoBarState(InfoBar* infobar,
    215                                                     bool animate) {
    216   GtkWindow* parent = platform_util::GetTopLevel(widget());
    217   BrowserWindowGtk* browser_window =
    218       BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent);
    219   if (browser_window)
    220     browser_window->SetInfoBarShowing(infobar, animate);
    221 }
    222