1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/gtk/infobars/infobar_gtk.h"
7 #include "base/debug/trace_event.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/themes/theme_properties.h"
12 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
13 #include "chrome/browser/ui/gtk/custom_button.h"
14 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
15 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
16 #include "chrome/browser/ui/gtk/gtk_util.h"
17 #include "chrome/browser/ui/gtk/infobars/infobar_container_gtk.h"
18 #include "content/public/browser/notification_source.h"
19 #include "content/public/browser/web_contents.h"
20 #include "ui/base/gtk/gtk_expanded_container.h"
21 #include "ui/base/gtk/gtk_hig_constants.h"
22 #include "ui/base/gtk/gtk_signal_registrar.h"
23 #include "ui/base/models/menu_model.h"
24 #include "ui/gfx/gtk_util.h"
25 #include "ui/gfx/image/image.h"
29 // Pixels between infobar elements.
30 const int kElementPadding
= 5;
32 // Extra padding on either end of info bar.
33 const int kLeftPadding
= 5;
34 const int kRightPadding
= 5;
39 // InfoBar --------------------------------------------------------------------
42 const int InfoBar::kSeparatorLineHeight
= 1;
43 const int InfoBar::kDefaultArrowTargetHeight
= 9;
44 const int InfoBar::kMaximumArrowTargetHeight
= 24;
45 const int InfoBar::kDefaultArrowTargetHalfWidth
= kDefaultArrowTargetHeight
;
46 const int InfoBar::kMaximumArrowTargetHalfWidth
= 14;
47 const int InfoBar::kDefaultBarTargetHeight
= 36;
50 // InfoBarGtk -----------------------------------------------------------------
53 const int InfoBarGtk::kEndOfLabelSpacing
= 6;
55 InfoBarGtk::InfoBarGtk(scoped_ptr
<InfoBarDelegate
> delegate
)
56 : InfoBar(delegate
.Pass()),
60 signals_(new ui::GtkSignalRegistrar
) {
63 InfoBarGtk::~InfoBarGtk() {
66 GdkColor
InfoBarGtk::GetBorderColor() const {
67 DCHECK(theme_service_
);
68 return theme_service_
->GetBorderColor();
71 int InfoBarGtk::AnimatingHeight() const {
72 return animation().is_animating() ? bar_target_height() : 0;
75 SkColor
InfoBarGtk::ConvertGetColor(ColorGetter getter
) {
77 (this->*getter
)(delegate()->GetInfoBarType(), &r
, &g
, &b
);
78 return SkColorSetARGB(255, 255 * r
, 255 * g
, 255 * b
);
81 void InfoBarGtk::GetTopColor(InfoBarDelegate::Type type
,
82 double* r
, double* g
, double* b
) {
83 GetBackgroundColor(InfoBar::GetTopColor(type
), r
, g
, b
);
86 void InfoBarGtk::GetBottomColor(InfoBarDelegate::Type type
,
87 double* r
, double* g
, double* b
) {
88 GetBackgroundColor(InfoBar::GetBottomColor(type
), r
, g
, b
);
91 void InfoBarGtk::PlatformSpecificSetOwner() {
93 DCHECK(!theme_service_
);
94 theme_service_
= GtkThemeService::GetFrom(Profile::FromBrowserContext(
95 owner()->web_contents()->GetBrowserContext()));
97 // Create |hbox_| and pad the sides.
98 hbox_
= gtk_hbox_new(FALSE
, kElementPadding
);
100 // Make the whole infor bar horizontally shrinkable.
101 gtk_widget_set_size_request(hbox_
, 0, -1);
103 GtkWidget
* padding
= gtk_alignment_new(0, 0, 1, 1);
104 gtk_alignment_set_padding(GTK_ALIGNMENT(padding
),
105 0, 0, kLeftPadding
, kRightPadding
);
107 bg_box_
= gtk_event_box_new();
108 gtk_widget_set_app_paintable(bg_box_
, TRUE
);
109 g_signal_connect(bg_box_
, "expose-event",
110 G_CALLBACK(OnBackgroundExposeThunk
), this);
111 gtk_container_add(GTK_CONTAINER(padding
), hbox_
);
112 gtk_container_add(GTK_CONTAINER(bg_box_
), padding
);
114 // Add the icon on the left, if any.
115 gfx::Image icon
= delegate()->GetIcon();
116 if (!icon
.IsEmpty()) {
117 GtkWidget
* image
= gtk_image_new_from_pixbuf(icon
.ToGdkPixbuf());
119 gtk_misc_set_alignment(GTK_MISC(image
), 0.5, 0.5);
121 gtk_box_pack_start(GTK_BOX(hbox_
), image
, FALSE
, FALSE
, 0);
124 close_button_
.reset(CustomDrawButton::CloseButtonBar(theme_service_
));
125 gtk_util::CenterWidgetInHBox(hbox_
, close_button_
->widget(), true, 0);
126 signals_
->Connect(close_button_
->widget(), "clicked",
127 G_CALLBACK(OnCloseButtonThunk
), this);
129 widget_
.Own(gtk_expanded_container_new());
130 gtk_container_add(GTK_CONTAINER(widget_
.get()), bg_box_
);
131 gtk_widget_set_size_request(widget_
.get(), -1, 0);
133 g_signal_connect(widget_
.get(), "child-size-request",
134 G_CALLBACK(OnChildSizeRequestThunk
),
137 registrar_
.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED
,
138 content::Source
<ThemeService
>(theme_service_
));
142 void InfoBarGtk::PlatformSpecificShow(bool animate
) {
146 gtk_widget_show_all(widget());
147 gtk_widget_set_size_request(widget(), -1, bar_height());
149 GdkWindow
* gdk_window
= gtk_widget_get_window(bg_box_
);
151 gdk_window_lower(gdk_window
);
154 void InfoBarGtk::PlatformSpecificOnCloseSoon() {
155 // We must close all menus and prevent any signals from being emitted while
156 // we are animating the info bar closed.
161 void InfoBarGtk::PlatformSpecificOnHeightsRecalculated() {
164 gtk_widget_set_size_request(bg_box_
, -1, bar_target_height());
165 gtk_expanded_container_move(GTK_EXPANDED_CONTAINER(widget()),
167 bar_height() - bar_target_height());
169 gtk_widget_set_size_request(widget(), -1, bar_height());
170 gtk_widget_queue_draw(widget());
173 void InfoBarGtk::Observe(int type
,
174 const content::NotificationSource
& source
,
175 const content::NotificationDetails
& details
) {
180 void InfoBarGtk::ForceCloseButtonToUseChromeTheme() {
181 close_button_
->ForceChromeTheme();
184 GtkWidget
* InfoBarGtk::CreateLabel(const std::string
& text
) {
185 DCHECK(theme_service_
);
186 return theme_service_
->BuildLabel(text
, ui::kGdkBlack
);
189 GtkWidget
* InfoBarGtk::CreateLinkButton(const std::string
& text
) {
190 DCHECK(theme_service_
);
191 return theme_service_
->BuildChromeLinkButton(text
);
195 GtkWidget
* InfoBarGtk::CreateMenuButton(const std::string
& text
) {
196 GtkWidget
* button
= gtk_button_new();
197 GtkWidget
* former_child
= gtk_bin_get_child(GTK_BIN(button
));
199 gtk_container_remove(GTK_CONTAINER(button
), former_child
);
201 GtkWidget
* hbox
= gtk_hbox_new(FALSE
, 0);
203 GtkWidget
* label
= gtk_label_new(text
.c_str());
204 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
206 GtkWidget
* arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
207 gtk_box_pack_start(GTK_BOX(hbox
), arrow
, FALSE
, FALSE
, 0);
209 gtk_container_add(GTK_CONTAINER(button
), hbox
);
214 void InfoBarGtk::AddLabelWithInlineLink(const base::string16
& display_text
,
215 const base::string16
& link_text
,
217 GCallback callback
) {
219 GtkWidget
* link_button
= CreateLinkButton(base::UTF16ToUTF8(link_text
));
220 gtk_util::ForceFontSizePixels(
221 GTK_CHROME_LINK_BUTTON(link_button
)->label
, 13.4);
223 signals_
->Connect(link_button
, "clicked", callback
, this);
224 gtk_util::SetButtonTriggersNavigation(link_button
);
226 GtkWidget
* hbox
= gtk_hbox_new(FALSE
, 0);
227 // We want the link to be horizontally shrinkable, so that the Chrome
228 // window can be resized freely even with a very long link.
229 gtk_widget_set_size_request(hbox
, 0, -1);
230 gtk_box_pack_start(GTK_BOX(hbox_
), hbox
, TRUE
, TRUE
, 0);
232 // Need to insert the link inside the display text.
233 GtkWidget
* initial_label
= CreateLabel(
234 base::UTF16ToUTF8(display_text
.substr(0, link_offset
)));
235 GtkWidget
* trailing_label
= CreateLabel(
236 base::UTF16ToUTF8(display_text
.substr(link_offset
)));
238 gtk_util::ForceFontSizePixels(initial_label
, 13.4);
239 gtk_util::ForceFontSizePixels(trailing_label
, 13.4);
241 // TODO(joth): None of the label widgets are set as shrinkable here, meaning
242 // the text will run under the close button etc. when the width is restricted,
243 // rather than eliding.
245 // We don't want any spacing between the elements, so we pack them into
246 // this hbox that doesn't use kElementPadding.
247 gtk_box_pack_start(GTK_BOX(hbox
), initial_label
, FALSE
, FALSE
, 0);
248 gtk_util::CenterWidgetInHBox(hbox
, link_button
, false, 0);
249 gtk_box_pack_start(GTK_BOX(hbox
), trailing_label
, FALSE
, FALSE
, 0);
252 void InfoBarGtk::ShowMenuWithModel(GtkWidget
* sender
,
253 MenuGtk::Delegate
* delegate
,
254 ui::MenuModel
* model
) {
255 menu_
.reset(new MenuGtk(delegate
, model
));
256 menu_
->PopupForWidget(sender
, 1, gtk_get_current_event_time());
259 void InfoBarGtk::GetBackgroundColor(SkColor color
,
260 double* r
, double* g
, double* b
) {
261 DCHECK(theme_service_
);
262 if (theme_service_
->UsingNativeTheme())
263 color
= theme_service_
->GetColor(ThemeProperties::COLOR_TOOLBAR
);
264 *r
= SkColorGetR(color
) / 255.0;
265 *g
= SkColorGetG(color
) / 255.0;
266 *b
= SkColorGetB(color
) / 255.0;
269 void InfoBarGtk::UpdateBorderColor() {
271 gtk_widget_queue_draw(widget());
274 void InfoBarGtk::OnCloseButton(GtkWidget
* button
) {
275 // If we're not owned, we're already closing, so don't call
276 // InfoBarDismissed(), since this can lead to us double-recording dismissals.
278 delegate()->InfoBarDismissed();
282 gboolean
InfoBarGtk::OnBackgroundExpose(GtkWidget
* sender
,
283 GdkEventExpose
* event
) {
284 TRACE_EVENT0("ui::gtk", "InfoBarGtk::OnBackgroundExpose");
285 DCHECK(theme_service_
);
287 GtkAllocation allocation
;
288 gtk_widget_get_allocation(sender
, &allocation
);
289 const int height
= allocation
.height
;
291 cairo_t
* cr
= gdk_cairo_create(gtk_widget_get_window(sender
));
292 gdk_cairo_rectangle(cr
, &event
->area
);
295 cairo_pattern_t
* pattern
= cairo_pattern_create_linear(0, 0, 0, height
);
297 double top_r
, top_g
, top_b
;
298 GetTopColor(delegate()->GetInfoBarType(), &top_r
, &top_g
, &top_b
);
299 cairo_pattern_add_color_stop_rgb(pattern
, 0.0, top_r
, top_g
, top_b
);
301 double bottom_r
, bottom_g
, bottom_b
;
302 GetBottomColor(delegate()->GetInfoBarType(), &bottom_r
, &bottom_g
, &bottom_b
);
303 cairo_pattern_add_color_stop_rgb(
304 pattern
, 1.0, bottom_r
, bottom_g
, bottom_b
);
305 cairo_set_source(cr
, pattern
);
307 cairo_pattern_destroy(pattern
);
309 // Draw the bottom border.
310 GdkColor border_color
= GetBorderColor();
311 cairo_set_source_rgb(cr
, border_color
.red
/ 65535.0,
312 border_color
.green
/ 65535.0,
313 border_color
.blue
/ 65535.0);
314 cairo_set_line_width(cr
, 1.0);
315 cairo_move_to(cr
, 0, allocation
.height
- 0.5);
316 cairo_rel_line_to(cr
, allocation
.width
, 0);
322 static_cast<InfoBarContainerGtk
*>(container())->
323 PaintInfobarBitsOn(sender
, event
, this);
329 void InfoBarGtk::OnChildSizeRequest(GtkWidget
* expanded
,
331 GtkRequisition
* requisition
) {
332 requisition
->height
= -1;