- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / gtk / infobars / infobar_gtk.cc
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.
4
5 #include "chrome/browser/ui/gtk/infobars/infobar_gtk.h"
6
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"
26
27 namespace {
28
29 // Pixels between infobar elements.
30 const int kElementPadding = 5;
31
32 // Extra padding on either end of info bar.
33 const int kLeftPadding = 5;
34 const int kRightPadding = 5;
35
36 }  // namespace
37
38
39 // InfoBar --------------------------------------------------------------------
40
41 // static
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;
48
49
50 // InfoBarGtk -----------------------------------------------------------------
51
52 // static
53 const int InfoBarGtk::kEndOfLabelSpacing = 6;
54
55 InfoBarGtk::InfoBarGtk(InfoBarService* owner, InfoBarDelegate* delegate)
56     : InfoBar(owner, delegate),
57       bg_box_(NULL),
58       hbox_(NULL),
59       theme_service_(NULL),
60       signals_(new ui::GtkSignalRegistrar) {
61 }
62
63 InfoBarGtk::~InfoBarGtk() {
64 }
65
66 void InfoBarGtk::InitWidgets() {
67   DCHECK(owner());
68   DCHECK(!theme_service_);
69   theme_service_ = GtkThemeService::GetFrom(Profile::FromBrowserContext(
70       owner()->web_contents()->GetBrowserContext()));
71
72   // Create |hbox_| and pad the sides.
73   hbox_ = gtk_hbox_new(FALSE, kElementPadding);
74
75   // Make the whole infor bar horizontally shrinkable.
76   gtk_widget_set_size_request(hbox_, 0, -1);
77
78   GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1);
79   gtk_alignment_set_padding(GTK_ALIGNMENT(padding),
80                             0, 0, kLeftPadding, kRightPadding);
81
82   bg_box_ = gtk_event_box_new();
83   gtk_widget_set_app_paintable(bg_box_, TRUE);
84   g_signal_connect(bg_box_, "expose-event",
85                    G_CALLBACK(OnBackgroundExposeThunk), this);
86   gtk_container_add(GTK_CONTAINER(padding), hbox_);
87   gtk_container_add(GTK_CONTAINER(bg_box_), padding);
88
89   // Add the icon on the left, if any.
90   gfx::Image icon = delegate()->GetIcon();
91   if (!icon.IsEmpty()) {
92     GtkWidget* image = gtk_image_new_from_pixbuf(icon.ToGdkPixbuf());
93
94     gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5);
95
96     gtk_box_pack_start(GTK_BOX(hbox_), image, FALSE, FALSE, 0);
97   }
98
99   close_button_.reset(CustomDrawButton::CloseButtonBar(theme_service_));
100   gtk_util::CenterWidgetInHBox(hbox_, close_button_->widget(), true, 0);
101   signals_->Connect(close_button_->widget(), "clicked",
102                     G_CALLBACK(OnCloseButtonThunk), this);
103
104   widget_.Own(gtk_expanded_container_new());
105   gtk_container_add(GTK_CONTAINER(widget_.get()), bg_box_);
106   gtk_widget_set_size_request(widget_.get(), -1, 0);
107
108   g_signal_connect(widget_.get(), "child-size-request",
109                    G_CALLBACK(OnChildSizeRequestThunk),
110                    this);
111
112   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
113                  content::Source<ThemeService>(theme_service_));
114   UpdateBorderColor();
115 }
116
117 GdkColor InfoBarGtk::GetBorderColor() const {
118   DCHECK(theme_service_);
119   return theme_service_->GetBorderColor();
120 }
121
122 int InfoBarGtk::AnimatingHeight() const {
123   return animation().is_animating() ? bar_target_height() : 0;
124 }
125
126 SkColor InfoBarGtk::ConvertGetColor(ColorGetter getter) {
127   double r, g, b;
128   (this->*getter)(delegate()->GetInfoBarType(), &r, &g, &b);
129   return SkColorSetARGB(255, 255 * r, 255 * g, 255 * b);
130 }
131
132 void InfoBarGtk::GetTopColor(InfoBarDelegate::Type type,
133                              double* r, double* g, double* b) {
134   GetBackgroundColor(GetInfoBarTopColor(type), r, g, b);
135 }
136
137 void InfoBarGtk::GetBottomColor(InfoBarDelegate::Type type,
138                                 double* r, double* g, double* b) {
139   GetBackgroundColor(GetInfoBarBottomColor(type), r, g, b);
140 }
141
142 void InfoBarGtk::PlatformSpecificShow(bool animate) {
143   DCHECK(bg_box_);
144
145   DCHECK(widget());
146   gtk_widget_show_all(widget());
147   gtk_widget_set_size_request(widget(), -1, bar_height());
148
149   GdkWindow* gdk_window = gtk_widget_get_window(bg_box_);
150   if (gdk_window)
151     gdk_window_lower(gdk_window);
152 }
153
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.
157   menu_.reset();
158   signals_.reset();
159 }
160
161 void InfoBarGtk::PlatformSpecificOnHeightsRecalculated() {
162   DCHECK(bg_box_);
163   DCHECK(widget());
164   gtk_widget_set_size_request(bg_box_, -1, bar_target_height());
165   gtk_expanded_container_move(GTK_EXPANDED_CONTAINER(widget()),
166                               bg_box_, 0,
167                               bar_height() - bar_target_height());
168
169   gtk_widget_set_size_request(widget(), -1, bar_height());
170   gtk_widget_queue_draw(widget());
171 }
172
173 void InfoBarGtk::Observe(int type,
174                          const content::NotificationSource& source,
175                          const content::NotificationDetails& details) {
176   DCHECK(widget());
177   UpdateBorderColor();
178 }
179
180 void InfoBarGtk::ForceCloseButtonToUseChromeTheme() {
181   close_button_->ForceChromeTheme();
182 }
183
184 GtkWidget* InfoBarGtk::CreateLabel(const std::string& text) {
185   DCHECK(theme_service_);
186   return theme_service_->BuildLabel(text, ui::kGdkBlack);
187 }
188
189 GtkWidget* InfoBarGtk::CreateLinkButton(const std::string& text) {
190   DCHECK(theme_service_);
191   return theme_service_->BuildChromeLinkButton(text);
192 }
193
194 // static
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));
198   if (former_child)
199     gtk_container_remove(GTK_CONTAINER(button), former_child);
200
201   GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
202
203   GtkWidget* label = gtk_label_new(text.c_str());
204   gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
205
206   GtkWidget* arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
207   gtk_box_pack_start(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
208
209   gtk_container_add(GTK_CONTAINER(button), hbox);
210
211   return button;
212 }
213
214 void InfoBarGtk::AddLabelWithInlineLink(const string16& display_text,
215                                         const string16& link_text,
216                                         size_t link_offset,
217                                         GCallback callback) {
218   DCHECK(hbox_);
219   GtkWidget* link_button = CreateLinkButton(UTF16ToUTF8(link_text));
220   gtk_util::ForceFontSizePixels(
221       GTK_CHROME_LINK_BUTTON(link_button)->label, 13.4);
222   DCHECK(callback);
223   signals_->Connect(link_button, "clicked", callback, this);
224   gtk_util::SetButtonTriggersNavigation(link_button);
225
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);
231
232   // Need to insert the link inside the display text.
233   GtkWidget* initial_label = CreateLabel(
234       UTF16ToUTF8(display_text.substr(0, link_offset)));
235   GtkWidget* trailing_label = CreateLabel(
236       UTF16ToUTF8(display_text.substr(link_offset)));
237
238   gtk_util::ForceFontSizePixels(initial_label, 13.4);
239   gtk_util::ForceFontSizePixels(trailing_label, 13.4);
240
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.
244
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);
250 }
251
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());
257 }
258
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;
267 }
268
269 void InfoBarGtk::UpdateBorderColor() {
270   DCHECK(widget());
271   gtk_widget_queue_draw(widget());
272 }
273
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.
277   if (delegate() && owner())
278     delegate()->InfoBarDismissed();
279   RemoveSelf();
280 }
281
282 gboolean InfoBarGtk::OnBackgroundExpose(GtkWidget* sender,
283                                         GdkEventExpose* event) {
284   TRACE_EVENT0("ui::gtk", "InfoBarGtk::OnBackgroundExpose");
285   DCHECK(theme_service_);
286
287   GtkAllocation allocation;
288   gtk_widget_get_allocation(sender, &allocation);
289   const int height = allocation.height;
290
291   cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(sender));
292   gdk_cairo_rectangle(cr, &event->area);
293   cairo_clip(cr);
294
295   cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height);
296
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);
300
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);
306   cairo_paint(cr);
307   cairo_pattern_destroy(pattern);
308
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);
317   cairo_stroke(cr);
318
319   cairo_destroy(cr);
320
321   if (container()) {
322     static_cast<InfoBarContainerGtk*>(container())->
323         PaintInfobarBitsOn(sender, event, this);
324   }
325
326   return FALSE;
327 }
328
329 void InfoBarGtk::OnChildSizeRequest(GtkWidget* expanded,
330                                     GtkWidget* child,
331                                     GtkRequisition* requisition) {
332   requisition->height = -1;
333 }