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