| OLD | NEW |
| (Empty) |
| 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(scoped_ptr<InfoBarDelegate> delegate) | |
| 56 : InfoBar(delegate.Pass()), | |
| 57 bg_box_(NULL), | |
| 58 hbox_(NULL), | |
| 59 theme_service_(NULL), | |
| 60 signals_(new ui::GtkSignalRegistrar) { | |
| 61 } | |
| 62 | |
| 63 InfoBarGtk::~InfoBarGtk() { | |
| 64 } | |
| 65 | |
| 66 GdkColor InfoBarGtk::GetBorderColor() const { | |
| 67 DCHECK(theme_service_); | |
| 68 return theme_service_->GetBorderColor(); | |
| 69 } | |
| 70 | |
| 71 int InfoBarGtk::AnimatingHeight() const { | |
| 72 return animation().is_animating() ? bar_target_height() : 0; | |
| 73 } | |
| 74 | |
| 75 SkColor InfoBarGtk::ConvertGetColor(ColorGetter getter) { | |
| 76 double r, g, b; | |
| 77 (this->*getter)(delegate()->GetInfoBarType(), &r, &g, &b); | |
| 78 return SkColorSetARGB(255, 255 * r, 255 * g, 255 * b); | |
| 79 } | |
| 80 | |
| 81 void InfoBarGtk::GetTopColor(InfoBarDelegate::Type type, | |
| 82 double* r, double* g, double* b) { | |
| 83 GetBackgroundColor(InfoBar::GetTopColor(type), r, g, b); | |
| 84 } | |
| 85 | |
| 86 void InfoBarGtk::GetBottomColor(InfoBarDelegate::Type type, | |
| 87 double* r, double* g, double* b) { | |
| 88 GetBackgroundColor(InfoBar::GetBottomColor(type), r, g, b); | |
| 89 } | |
| 90 | |
| 91 void InfoBarGtk::PlatformSpecificSetOwner() { | |
| 92 DCHECK(owner()); | |
| 93 DCHECK(!theme_service_); | |
| 94 theme_service_ = GtkThemeService::GetFrom(Profile::FromBrowserContext( | |
| 95 owner()->web_contents()->GetBrowserContext())); | |
| 96 | |
| 97 // Create |hbox_| and pad the sides. | |
| 98 hbox_ = gtk_hbox_new(FALSE, kElementPadding); | |
| 99 | |
| 100 // Make the whole infor bar horizontally shrinkable. | |
| 101 gtk_widget_set_size_request(hbox_, 0, -1); | |
| 102 | |
| 103 GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1); | |
| 104 gtk_alignment_set_padding(GTK_ALIGNMENT(padding), | |
| 105 0, 0, kLeftPadding, kRightPadding); | |
| 106 | |
| 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); | |
| 113 | |
| 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()); | |
| 118 | |
| 119 gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5); | |
| 120 | |
| 121 gtk_box_pack_start(GTK_BOX(hbox_), image, FALSE, FALSE, 0); | |
| 122 } | |
| 123 | |
| 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); | |
| 128 | |
| 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); | |
| 132 | |
| 133 g_signal_connect(widget_.get(), "child-size-request", | |
| 134 G_CALLBACK(OnChildSizeRequestThunk), | |
| 135 this); | |
| 136 | |
| 137 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | |
| 138 content::Source<ThemeService>(theme_service_)); | |
| 139 UpdateBorderColor(); | |
| 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 base::string16& display_text, | |
| 215 const base::string16& link_text, | |
| 216 size_t link_offset, | |
| 217 GCallback callback) { | |
| 218 DCHECK(hbox_); | |
| 219 GtkWidget* link_button = CreateLinkButton(base::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 base::UTF16ToUTF8(display_text.substr(0, link_offset))); | |
| 235 GtkWidget* trailing_label = CreateLabel( | |
| 236 base::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 (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 } | |
| OLD | NEW |