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.
AnimateClosingForDelegate(GtkWidget * infobar_widget,gpointer info_bar_delegate)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.
ClosingForDelegate(GtkWidget * infobar_widget,gpointer info_bar_delegate)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.
SumAnimatingBarHeight(GtkWidget * widget,gpointer userdata)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
InfoBarContainerGtk(Profile * profile)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
~InfoBarContainerGtk()83 InfoBarContainerGtk::~InfoBarContainerGtk() {
84 ChangeTabContents(NULL);
85
86 container_.Destroy();
87 }
88
ChangeTabContents(TabContents * contents)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
RemoveDelegate(InfoBarDelegate * delegate)108 void InfoBarContainerGtk::RemoveDelegate(InfoBarDelegate* delegate) {
109 tab_contents_->RemoveInfoBar(delegate);
110 }
111
TotalHeightOfAnimatingBars() const112 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
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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
UpdateInfoBars()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
ShowArrowForDelegate(InfoBarDelegate * delegate,bool animate)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
AddInfoBar(InfoBarDelegate * delegate,bool animate)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
RemoveInfoBar(InfoBarDelegate * delegate,bool animate)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
UpdateToolbarInfoBarState(InfoBar * infobar,bool animate)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