• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_gtk.h"
6 
7 #include <gtk/gtk.h>
8 
9 #include "base/utf_string_conversions.h"
10 #include "chrome/browser/platform_util.h"
11 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
12 #include "chrome/browser/ui/gtk/custom_button.h"
13 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
14 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
15 #include "chrome/browser/ui/gtk/gtk_util.h"
16 #include "chrome/browser/ui/gtk/infobars/infobar_container_gtk.h"
17 #include "content/common/notification_service.h"
18 #include "ui/gfx/gtk_util.h"
19 
20 extern const int InfoBar::kInfoBarHeight = 37;
21 
22 namespace {
23 
24 // Pixels between infobar elements.
25 const int kElementPadding = 5;
26 
27 // Extra padding on either end of info bar.
28 const int kLeftPadding = 5;
29 const int kRightPadding = 5;
30 
31 }  // namespace
32 
33 // static
34 const int InfoBar::kEndOfLabelSpacing = 6;
35 const int InfoBar::kButtonButtonSpacing = 3;
36 
InfoBar(InfoBarDelegate * delegate)37 InfoBar::InfoBar(InfoBarDelegate* delegate)
38     : container_(NULL),
39       delegate_(delegate),
40       theme_service_(NULL),
41       arrow_model_(this) {
42   // Create |hbox_| and pad the sides.
43   hbox_ = gtk_hbox_new(FALSE, kElementPadding);
44 
45   // Make the whole infor bar horizontally shrinkable.
46   gtk_widget_set_size_request(hbox_, 0, -1);
47 
48   GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1);
49   gtk_alignment_set_padding(GTK_ALIGNMENT(padding),
50       0, 0, kLeftPadding, kRightPadding);
51 
52   bg_box_ = gtk_event_box_new();
53   gtk_widget_set_app_paintable(bg_box_, TRUE);
54   g_signal_connect(bg_box_, "expose-event",
55                    G_CALLBACK(OnBackgroundExposeThunk), this);
56   gtk_container_add(GTK_CONTAINER(padding), hbox_);
57   gtk_container_add(GTK_CONTAINER(bg_box_), padding);
58   gtk_widget_set_size_request(bg_box_, -1, kInfoBarHeight);
59 
60   // Add the icon on the left, if any.
61   SkBitmap* icon = delegate->GetIcon();
62   if (icon) {
63     GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(icon);
64     GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf);
65     g_object_unref(pixbuf);
66 
67     gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5);
68 
69     gtk_box_pack_start(GTK_BOX(hbox_), image, FALSE, FALSE, 0);
70   }
71 
72   close_button_.reset(CustomDrawButton::CloseButton(NULL));
73   gtk_util::CenterWidgetInHBox(hbox_, close_button_->widget(), true, 0);
74   g_signal_connect(close_button_->widget(), "clicked",
75                    G_CALLBACK(OnCloseButtonThunk), this);
76 
77   slide_widget_.reset(new SlideAnimatorGtk(bg_box_,
78                                            SlideAnimatorGtk::DOWN,
79                                            0, true, true, this));
80   // We store a pointer back to |this| so we can refer to it from the infobar
81   // container.
82   g_object_set_data(G_OBJECT(slide_widget_->widget()), "info-bar", this);
83 }
84 
~InfoBar()85 InfoBar::~InfoBar() {
86 }
87 
widget()88 GtkWidget* InfoBar::widget() {
89   return slide_widget_->widget();
90 }
91 
AnimateOpen()92 void InfoBar::AnimateOpen() {
93   slide_widget_->Open();
94 
95   gtk_widget_show_all(bg_box_);
96   if (bg_box_->window)
97     gdk_window_lower(bg_box_->window);
98 }
99 
Open()100 void InfoBar::Open() {
101   slide_widget_->OpenWithoutAnimation();
102 
103   gtk_widget_show_all(bg_box_);
104   if (bg_box_->window)
105     gdk_window_lower(bg_box_->window);
106 }
107 
AnimateClose()108 void InfoBar::AnimateClose() {
109   slide_widget_->Close();
110 }
111 
Close()112 void InfoBar::Close() {
113   if (delegate_) {
114     delegate_->InfoBarClosed();
115     delegate_ = NULL;
116   }
117   delete this;
118 }
119 
IsAnimating()120 bool InfoBar::IsAnimating() {
121   return slide_widget_->IsAnimating();
122 }
123 
IsClosing()124 bool InfoBar::IsClosing() {
125   return slide_widget_->IsClosing();
126 }
127 
ShowArrowFor(InfoBar * other,bool animate)128 void InfoBar::ShowArrowFor(InfoBar* other, bool animate) {
129   arrow_model_.ShowArrowFor(other, animate);
130 }
131 
PaintStateChanged()132 void InfoBar::PaintStateChanged() {
133   gtk_widget_queue_draw(widget());
134 }
135 
RemoveInfoBar() const136 void InfoBar::RemoveInfoBar() const {
137   container_->RemoveDelegate(delegate_);
138 }
139 
Closed()140 void InfoBar::Closed() {
141   Close();
142 }
143 
SetThemeProvider(GtkThemeService * theme_service)144 void InfoBar::SetThemeProvider(GtkThemeService* theme_service) {
145   if (theme_service_) {
146     NOTREACHED();
147     return;
148   }
149 
150   theme_service_ = theme_service;
151   registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
152                  NotificationService::AllSources());
153   UpdateBorderColor();
154 }
155 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)156 void InfoBar::Observe(NotificationType type,
157                       const NotificationSource& source,
158                       const NotificationDetails& details) {
159   UpdateBorderColor();
160 }
161 
AddLabelWithInlineLink(const string16 & display_text,const string16 & link_text,size_t link_offset,GCallback callback)162 void InfoBar::AddLabelWithInlineLink(const string16& display_text,
163                                      const string16& link_text,
164                                      size_t link_offset,
165                                      GCallback callback) {
166   GtkWidget* link_button = gtk_chrome_link_button_new(
167       UTF16ToUTF8(link_text).c_str());
168   gtk_chrome_link_button_set_use_gtk_theme(
169       GTK_CHROME_LINK_BUTTON(link_button), FALSE);
170   gtk_util::ForceFontSizePixels(
171       GTK_CHROME_LINK_BUTTON(link_button)->label, 13.4);
172   DCHECK(callback);
173   g_signal_connect(link_button, "clicked", callback, this);
174   gtk_util::SetButtonTriggersNavigation(link_button);
175 
176   GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
177   // We want the link to be horizontally shrinkable, so that the Chrome
178   // window can be resized freely even with a very long link.
179   gtk_widget_set_size_request(hbox, 0, -1);
180   gtk_box_pack_start(GTK_BOX(hbox_), hbox, TRUE, TRUE, 0);
181 
182   // Need to insert the link inside the display text.
183   GtkWidget* initial_label = gtk_label_new(
184       UTF16ToUTF8(display_text.substr(0, link_offset)).c_str());
185   GtkWidget* trailing_label = gtk_label_new(
186       UTF16ToUTF8(display_text.substr(link_offset)).c_str());
187 
188   gtk_util::ForceFontSizePixels(initial_label, 13.4);
189   gtk_util::ForceFontSizePixels(trailing_label, 13.4);
190 
191   // TODO(joth): None of the label widgets are set as shrinkable here, meaning
192   // the text will run under the close button etc. when the width is restricted,
193   // rather than eliding.
194   gtk_widget_modify_fg(initial_label, GTK_STATE_NORMAL, &gtk_util::kGdkBlack);
195   gtk_widget_modify_fg(trailing_label, GTK_STATE_NORMAL, &gtk_util::kGdkBlack);
196 
197   // We don't want any spacing between the elements, so we pack them into
198   // this hbox that doesn't use kElementPadding.
199   gtk_box_pack_start(GTK_BOX(hbox), initial_label, FALSE, FALSE, 0);
200   gtk_util::CenterWidgetInHBox(hbox, link_button, false, 0);
201   gtk_box_pack_start(GTK_BOX(hbox), trailing_label, FALSE, FALSE, 0);
202 }
203 
GetTopColor(InfoBarDelegate::Type type,double * r,double * g,double * b)204 void InfoBar::GetTopColor(InfoBarDelegate::Type type,
205                           double* r, double* g, double *b) {
206   // These constants are copied from corresponding skia constants from
207   // browser/ui/views/infobars/infobars.cc, and then changed into 0-1 ranged
208   // values for cairo.
209   switch (type) {
210     case InfoBarDelegate::WARNING_TYPE:
211       *r = 255.0 / 255.0;
212       *g = 242.0 / 255.0;
213       *b = 183.0 / 255.0;
214       break;
215     case InfoBarDelegate::PAGE_ACTION_TYPE:
216       *r = 218.0 / 255.0;
217       *g = 231.0 / 255.0;
218       *b = 249.0 / 255.0;
219       break;
220   }
221 }
222 
GetBottomColor(InfoBarDelegate::Type type,double * r,double * g,double * b)223 void InfoBar::GetBottomColor(InfoBarDelegate::Type type,
224                              double* r, double* g, double *b) {
225   switch (type) {
226     case InfoBarDelegate::WARNING_TYPE:
227       *r = 250.0 / 255.0;
228       *g = 230.0 / 255.0;
229       *b = 145.0 / 255.0;
230       break;
231     case InfoBarDelegate::PAGE_ACTION_TYPE:
232       *r = 179.0 / 255.0;
233       *g = 202.0 / 255.0;
234       *b = 231.0 / 255.0;
235       break;
236   }
237 }
238 
UpdateBorderColor()239 void InfoBar::UpdateBorderColor() {
240   gtk_widget_queue_draw(widget());
241 }
242 
OnCloseButton(GtkWidget * button)243 void InfoBar::OnCloseButton(GtkWidget* button) {
244   if (delegate_)
245     delegate_->InfoBarDismissed();
246   RemoveInfoBar();
247 }
248 
OnBackgroundExpose(GtkWidget * sender,GdkEventExpose * event)249 gboolean InfoBar::OnBackgroundExpose(GtkWidget* sender,
250                                      GdkEventExpose* event) {
251   const int height = sender->allocation.height;
252 
253   cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(sender->window));
254   gdk_cairo_rectangle(cr, &event->area);
255   cairo_clip(cr);
256 
257   cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height);
258 
259   double top_r, top_g, top_b;
260   GetTopColor(delegate_->GetInfoBarType(), &top_r, &top_g, &top_b);
261   cairo_pattern_add_color_stop_rgb(pattern, 0.0, top_r, top_g, top_b);
262 
263   double bottom_r, bottom_g, bottom_b;
264   GetBottomColor(delegate_->GetInfoBarType(), &bottom_r, &bottom_g, &bottom_b);
265   cairo_pattern_add_color_stop_rgb(
266       pattern, 1.0, bottom_r, bottom_g, bottom_b);
267   cairo_set_source(cr, pattern);
268   cairo_paint(cr);
269   cairo_pattern_destroy(pattern);
270 
271   // Draw the bottom border.
272   GdkColor border_color = theme_service_->GetBorderColor();
273   cairo_set_source_rgb(cr, border_color.red / 65535.0,
274                            border_color.green / 65535.0,
275                            border_color.blue / 65535.0);
276   cairo_set_line_width(cr, 1.0);
277   int y = sender->allocation.height;
278   cairo_move_to(cr, 0, y - 0.5);
279   cairo_rel_line_to(cr, sender->allocation.width, 0);
280   cairo_stroke(cr);
281 
282   cairo_destroy(cr);
283 
284   if (!arrow_model_.NeedToDrawInfoBarArrow())
285     return FALSE;
286 
287   GtkWindow* parent = platform_util::GetTopLevel(widget());
288   BrowserWindowGtk* browser_window =
289       BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent);
290   int x = browser_window ?
291       browser_window->GetXPositionOfLocationIcon(sender) : 0;
292 
293   size_t size = InfoBarArrowModel::kDefaultArrowSize;
294   gfx::Rect arrow_bounds(x - size, y - size, 2 * size, size);
295   arrow_model_.Paint(sender, event, arrow_bounds, border_color);
296 
297   return FALSE;
298 }
299