| OLD | NEW |
| (Empty) |
| 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/gtk/infobar_gtk.h" | |
| 6 | |
| 7 #include <gtk/gtk.h> | |
| 8 | |
| 9 #include "base/utf_string_conversions.h" | |
| 10 #include "chrome/browser/gtk/browser_window_gtk.h" | |
| 11 #include "chrome/browser/gtk/custom_button.h" | |
| 12 #include "chrome/browser/gtk/gtk_chrome_link_button.h" | |
| 13 #include "chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h" | |
| 14 #include "chrome/browser/gtk/gtk_theme_provider.h" | |
| 15 #include "chrome/browser/gtk/gtk_util.h" | |
| 16 #include "chrome/browser/gtk/infobar_container_gtk.h" | |
| 17 #include "chrome/browser/platform_util.h" | |
| 18 #include "chrome/common/notification_service.h" | |
| 19 #include "gfx/gtk_util.h" | |
| 20 | |
| 21 extern const int InfoBar::kInfoBarHeight = 37; | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 // Spacing after message (and before buttons). | |
| 26 const int kEndOfLabelSpacing = 6; | |
| 27 // Spacing between buttons. | |
| 28 const int kButtonButtonSpacing = 3; | |
| 29 | |
| 30 // Pixels between infobar elements. | |
| 31 const int kElementPadding = 5; | |
| 32 | |
| 33 // Extra padding on either end of info bar. | |
| 34 const int kLeftPadding = 5; | |
| 35 const int kRightPadding = 5; | |
| 36 | |
| 37 } // namespace | |
| 38 | |
| 39 InfoBar::InfoBar(InfoBarDelegate* delegate) | |
| 40 : container_(NULL), | |
| 41 delegate_(delegate), | |
| 42 theme_provider_(NULL), | |
| 43 arrow_model_(this) { | |
| 44 // Create |hbox_| and pad the sides. | |
| 45 hbox_ = gtk_hbox_new(FALSE, kElementPadding); | |
| 46 | |
| 47 // Make the whole infor bar horizontally shrinkable. | |
| 48 gtk_widget_set_size_request(hbox_, 0, -1); | |
| 49 | |
| 50 GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1); | |
| 51 gtk_alignment_set_padding(GTK_ALIGNMENT(padding), | |
| 52 0, 0, kLeftPadding, kRightPadding); | |
| 53 | |
| 54 bg_box_ = gtk_event_box_new(); | |
| 55 gtk_widget_set_app_paintable(bg_box_, TRUE); | |
| 56 g_signal_connect(bg_box_, "expose-event", | |
| 57 G_CALLBACK(OnBackgroundExposeThunk), this); | |
| 58 gtk_container_add(GTK_CONTAINER(padding), hbox_); | |
| 59 gtk_container_add(GTK_CONTAINER(bg_box_), padding); | |
| 60 gtk_widget_set_size_request(bg_box_, -1, kInfoBarHeight); | |
| 61 | |
| 62 // Add the icon on the left, if any. | |
| 63 SkBitmap* icon = delegate->GetIcon(); | |
| 64 if (icon) { | |
| 65 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(icon); | |
| 66 GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf); | |
| 67 g_object_unref(pixbuf); | |
| 68 gtk_box_pack_start(GTK_BOX(hbox_), image, FALSE, FALSE, 0); | |
| 69 } | |
| 70 | |
| 71 close_button_.reset(CustomDrawButton::CloseButton(NULL)); | |
| 72 gtk_util::CenterWidgetInHBox(hbox_, close_button_->widget(), true, 0); | |
| 73 g_signal_connect(close_button_->widget(), "clicked", | |
| 74 G_CALLBACK(OnCloseButtonThunk), this); | |
| 75 | |
| 76 slide_widget_.reset(new SlideAnimatorGtk(bg_box_, | |
| 77 SlideAnimatorGtk::DOWN, | |
| 78 0, true, true, this)); | |
| 79 // We store a pointer back to |this| so we can refer to it from the infobar | |
| 80 // container. | |
| 81 g_object_set_data(G_OBJECT(slide_widget_->widget()), "info-bar", this); | |
| 82 } | |
| 83 | |
| 84 InfoBar::~InfoBar() { | |
| 85 } | |
| 86 | |
| 87 GtkWidget* InfoBar::widget() { | |
| 88 return slide_widget_->widget(); | |
| 89 } | |
| 90 | |
| 91 void InfoBar::AnimateOpen() { | |
| 92 slide_widget_->Open(); | |
| 93 | |
| 94 gtk_widget_show_all(bg_box_); | |
| 95 if (bg_box_->window) | |
| 96 gdk_window_lower(bg_box_->window); | |
| 97 } | |
| 98 | |
| 99 void InfoBar::Open() { | |
| 100 slide_widget_->OpenWithoutAnimation(); | |
| 101 | |
| 102 gtk_widget_show_all(bg_box_); | |
| 103 if (bg_box_->window) | |
| 104 gdk_window_lower(bg_box_->window); | |
| 105 } | |
| 106 | |
| 107 void InfoBar::AnimateClose() { | |
| 108 slide_widget_->Close(); | |
| 109 } | |
| 110 | |
| 111 void InfoBar::Close() { | |
| 112 if (delegate_) { | |
| 113 delegate_->InfoBarClosed(); | |
| 114 delegate_ = NULL; | |
| 115 } | |
| 116 delete this; | |
| 117 } | |
| 118 | |
| 119 bool InfoBar::IsAnimating() { | |
| 120 return slide_widget_->IsAnimating(); | |
| 121 } | |
| 122 | |
| 123 bool InfoBar::IsClosing() { | |
| 124 return slide_widget_->IsClosing(); | |
| 125 } | |
| 126 | |
| 127 void InfoBar::ShowArrowFor(InfoBar* other, bool animate) { | |
| 128 arrow_model_.ShowArrowFor(other, animate); | |
| 129 } | |
| 130 | |
| 131 void InfoBar::PaintStateChanged() { | |
| 132 gtk_widget_queue_draw(widget()); | |
| 133 } | |
| 134 | |
| 135 void InfoBar::RemoveInfoBar() const { | |
| 136 container_->RemoveDelegate(delegate_); | |
| 137 } | |
| 138 | |
| 139 void InfoBar::Closed() { | |
| 140 Close(); | |
| 141 } | |
| 142 | |
| 143 void InfoBar::SetThemeProvider(GtkThemeProvider* theme_provider) { | |
| 144 if (theme_provider_) { | |
| 145 NOTREACHED(); | |
| 146 return; | |
| 147 } | |
| 148 | |
| 149 theme_provider_ = theme_provider; | |
| 150 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, | |
| 151 NotificationService::AllSources()); | |
| 152 UpdateBorderColor(); | |
| 153 } | |
| 154 | |
| 155 void InfoBar::Observe(NotificationType type, | |
| 156 const NotificationSource& source, | |
| 157 const NotificationDetails& details) { | |
| 158 UpdateBorderColor(); | |
| 159 } | |
| 160 | |
| 161 void InfoBar::AddLabelWithInlineLink(const string16& display_text, | |
| 162 const string16& link_text, | |
| 163 size_t link_offset, | |
| 164 GCallback callback) { | |
| 165 GtkWidget* link_button = gtk_chrome_link_button_new( | |
| 166 UTF16ToUTF8(link_text).c_str()); | |
| 167 gtk_chrome_link_button_set_use_gtk_theme( | |
| 168 GTK_CHROME_LINK_BUTTON(link_button), FALSE); | |
| 169 gtk_util::ForceFontSizePixels( | |
| 170 GTK_CHROME_LINK_BUTTON(link_button)->label, 13.4); | |
| 171 DCHECK(callback); | |
| 172 g_signal_connect(link_button, "clicked", callback, this); | |
| 173 gtk_util::SetButtonTriggersNavigation(link_button); | |
| 174 | |
| 175 GtkWidget* hbox = gtk_hbox_new(FALSE, 0); | |
| 176 // We want the link to be horizontally shrinkable, so that the Chrome | |
| 177 // window can be resized freely even with a very long link. | |
| 178 gtk_widget_set_size_request(hbox, 0, -1); | |
| 179 gtk_box_pack_start(GTK_BOX(hbox_), hbox, TRUE, TRUE, 0); | |
| 180 | |
| 181 // Need to insert the link inside the display text. | |
| 182 GtkWidget* initial_label = gtk_label_new( | |
| 183 UTF16ToUTF8(display_text.substr(0, link_offset)).c_str()); | |
| 184 GtkWidget* trailing_label = gtk_label_new( | |
| 185 UTF16ToUTF8(display_text.substr(link_offset)).c_str()); | |
| 186 | |
| 187 gtk_util::ForceFontSizePixels(initial_label, 13.4); | |
| 188 gtk_util::ForceFontSizePixels(trailing_label, 13.4); | |
| 189 | |
| 190 // TODO(joth): Unlike the AddLabelAndLink below, none of the label widgets | |
| 191 // are set as shrinkable here, meaning the text will run under the close | |
| 192 // button etc. when the width is restricted, rather than eliding. | |
| 193 gtk_widget_modify_fg(initial_label, GTK_STATE_NORMAL, >k_util::kGdkBlack); | |
| 194 gtk_widget_modify_fg(trailing_label, GTK_STATE_NORMAL, >k_util::kGdkBlack); | |
| 195 | |
| 196 // We don't want any spacing between the elements, so we pack them into | |
| 197 // this hbox that doesn't use kElementPadding. | |
| 198 gtk_box_pack_start(GTK_BOX(hbox), initial_label, FALSE, FALSE, 0); | |
| 199 gtk_util::CenterWidgetInHBox(hbox, link_button, false, 0); | |
| 200 gtk_box_pack_start(GTK_BOX(hbox), trailing_label, FALSE, FALSE, 0); | |
| 201 } | |
| 202 | |
| 203 // TODO(joth): This method factors out some common functionality between the | |
| 204 // various derived infobar classes, however the class hierarchy itself could | |
| 205 // use refactoring to reduce this duplication. http://crbug.com/38924 | |
| 206 void InfoBar::AddLabelAndLink(const string16& display_text, | |
| 207 const string16& link_text, | |
| 208 GCallback callback) { | |
| 209 GtkWidget* link_button = NULL; | |
| 210 if (!link_text.empty()) { | |
| 211 // If we have some link text, create the link button. | |
| 212 link_button = gtk_chrome_link_button_new(UTF16ToUTF8(link_text).c_str()); | |
| 213 gtk_chrome_link_button_set_use_gtk_theme( | |
| 214 GTK_CHROME_LINK_BUTTON(link_button), FALSE); | |
| 215 DCHECK(callback); | |
| 216 g_signal_connect(link_button, "clicked", callback, this); | |
| 217 gtk_util::SetButtonTriggersNavigation(link_button); | |
| 218 } | |
| 219 | |
| 220 GtkWidget* hbox = gtk_hbox_new(FALSE, 0); | |
| 221 // We want the link to be horizontally shrinkable, so that the Chrome | |
| 222 // window can be resized freely even with a very long link. | |
| 223 gtk_widget_set_size_request(hbox, 0, -1); | |
| 224 gtk_box_pack_start(GTK_BOX(hbox_), hbox, TRUE, TRUE, 0); | |
| 225 | |
| 226 if (link_button) | |
| 227 gtk_box_pack_end(GTK_BOX(hbox), link_button, FALSE, FALSE, 0); | |
| 228 GtkWidget* label = gtk_label_new(UTF16ToUTF8(display_text).c_str()); | |
| 229 gtk_util::ForceFontSizePixels(label, 13.4); | |
| 230 // In order to avoid the link_button and the label overlapping with each | |
| 231 // other, we make the label shrinkable. | |
| 232 gtk_widget_set_size_request(label, 0, -1); | |
| 233 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END); | |
| 234 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
| 235 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, >k_util::kGdkBlack); | |
| 236 gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); | |
| 237 } | |
| 238 | |
| 239 void InfoBar::GetTopColor(InfoBarDelegate::Type type, | |
| 240 double* r, double* g, double *b) { | |
| 241 // These constants are copied from corresponding skia constants from | |
| 242 // browser/ui/views/infobars/infobars.cc, and then changed into 0-1 ranged | |
| 243 // values for cairo. | |
| 244 switch (type) { | |
| 245 case InfoBarDelegate::WARNING_TYPE: | |
| 246 *r = 255.0 / 255.0; | |
| 247 *g = 242.0 / 255.0; | |
| 248 *b = 183.0 / 255.0; | |
| 249 break; | |
| 250 case InfoBarDelegate::PAGE_ACTION_TYPE: | |
| 251 *r = 218.0 / 255.0; | |
| 252 *g = 231.0 / 255.0; | |
| 253 *b = 249.0 / 255.0; | |
| 254 break; | |
| 255 } | |
| 256 } | |
| 257 | |
| 258 void InfoBar::GetBottomColor(InfoBarDelegate::Type type, | |
| 259 double* r, double* g, double *b) { | |
| 260 switch (type) { | |
| 261 case InfoBarDelegate::WARNING_TYPE: | |
| 262 *r = 250.0 / 255.0; | |
| 263 *g = 230.0 / 255.0; | |
| 264 *b = 145.0 / 255.0; | |
| 265 break; | |
| 266 case InfoBarDelegate::PAGE_ACTION_TYPE: | |
| 267 *r = 179.0 / 255.0; | |
| 268 *g = 202.0 / 255.0; | |
| 269 *b = 231.0 / 255.0; | |
| 270 break; | |
| 271 } | |
| 272 } | |
| 273 | |
| 274 void InfoBar::UpdateBorderColor() { | |
| 275 gtk_widget_queue_draw(widget()); | |
| 276 } | |
| 277 | |
| 278 void InfoBar::OnCloseButton(GtkWidget* button) { | |
| 279 if (delegate_) | |
| 280 delegate_->InfoBarDismissed(); | |
| 281 RemoveInfoBar(); | |
| 282 } | |
| 283 | |
| 284 gboolean InfoBar::OnBackgroundExpose(GtkWidget* sender, | |
| 285 GdkEventExpose* event) { | |
| 286 const int height = sender->allocation.height; | |
| 287 | |
| 288 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(sender->window)); | |
| 289 gdk_cairo_rectangle(cr, &event->area); | |
| 290 cairo_clip(cr); | |
| 291 | |
| 292 cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height); | |
| 293 | |
| 294 double top_r, top_g, top_b; | |
| 295 GetTopColor(delegate_->GetInfoBarType(), &top_r, &top_g, &top_b); | |
| 296 cairo_pattern_add_color_stop_rgb(pattern, 0.0, top_r, top_g, top_b); | |
| 297 | |
| 298 double bottom_r, bottom_g, bottom_b; | |
| 299 GetBottomColor(delegate_->GetInfoBarType(), &bottom_r, &bottom_g, &bottom_b); | |
| 300 cairo_pattern_add_color_stop_rgb( | |
| 301 pattern, 1.0, bottom_r, bottom_g, bottom_b); | |
| 302 cairo_set_source(cr, pattern); | |
| 303 cairo_paint(cr); | |
| 304 cairo_pattern_destroy(pattern); | |
| 305 | |
| 306 // Draw the bottom border. | |
| 307 GdkColor border_color = theme_provider_->GetBorderColor(); | |
| 308 cairo_set_source_rgb(cr, border_color.red / 65535.0, | |
| 309 border_color.green / 65535.0, | |
| 310 border_color.blue / 65535.0); | |
| 311 cairo_set_line_width(cr, 1.0); | |
| 312 int y = sender->allocation.height; | |
| 313 cairo_move_to(cr, 0, y - 0.5); | |
| 314 cairo_rel_line_to(cr, sender->allocation.width, 0); | |
| 315 cairo_stroke(cr); | |
| 316 | |
| 317 cairo_destroy(cr); | |
| 318 | |
| 319 if (!arrow_model_.NeedToDrawInfoBarArrow()) | |
| 320 return FALSE; | |
| 321 | |
| 322 GtkWindow* parent = platform_util::GetTopLevel(widget()); | |
| 323 BrowserWindowGtk* browser_window = | |
| 324 BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent); | |
| 325 int x = browser_window ? | |
| 326 browser_window->GetXPositionOfLocationIcon(sender) : 0; | |
| 327 | |
| 328 arrow_model_.Paint(sender, event, gfx::Point(x, y), border_color); | |
| 329 | |
| 330 return FALSE; | |
| 331 } | |
| 332 | |
| 333 // AlertInfoBar ---------------------------------------------------------------- | |
| 334 | |
| 335 class AlertInfoBar : public InfoBar { | |
| 336 public: | |
| 337 explicit AlertInfoBar(AlertInfoBarDelegate* delegate) | |
| 338 : InfoBar(delegate) { | |
| 339 AddLabelAndLink(delegate->GetMessageText(), string16(), NULL); | |
| 340 } | |
| 341 }; | |
| 342 | |
| 343 // LinkInfoBar ----------------------------------------------------------------- | |
| 344 | |
| 345 class LinkInfoBar : public InfoBar { | |
| 346 public: | |
| 347 explicit LinkInfoBar(LinkInfoBarDelegate* delegate) | |
| 348 : InfoBar(delegate) { | |
| 349 size_t link_offset; | |
| 350 string16 display_text = delegate->GetMessageTextWithOffset(&link_offset); | |
| 351 string16 link_text = delegate->GetLinkText(); | |
| 352 AddLabelWithInlineLink(display_text, link_text, link_offset, | |
| 353 G_CALLBACK(OnLinkClick)); | |
| 354 } | |
| 355 | |
| 356 private: | |
| 357 static void OnLinkClick(GtkWidget* button, LinkInfoBar* link_info_bar) { | |
| 358 if (link_info_bar->delegate_->AsLinkInfoBarDelegate()-> | |
| 359 LinkClicked(gtk_util::DispositionForCurrentButtonPressEvent())) { | |
| 360 link_info_bar->RemoveInfoBar(); | |
| 361 } | |
| 362 } | |
| 363 }; | |
| 364 | |
| 365 // ConfirmInfoBar -------------------------------------------------------------- | |
| 366 | |
| 367 class ConfirmInfoBar : public InfoBar { | |
| 368 public: | |
| 369 explicit ConfirmInfoBar(ConfirmInfoBarDelegate* delegate); | |
| 370 | |
| 371 private: | |
| 372 // Adds a button to the info bar by type. It will do nothing if the delegate | |
| 373 // doesn't specify a button of the given type. | |
| 374 void AddButton(ConfirmInfoBarDelegate::InfoBarButton type); | |
| 375 | |
| 376 CHROMEGTK_CALLBACK_0(ConfirmInfoBar, void, OnOkButton); | |
| 377 CHROMEGTK_CALLBACK_0(ConfirmInfoBar, void, OnCancelButton); | |
| 378 CHROMEGTK_CALLBACK_0(ConfirmInfoBar, void, OnLinkClicked); | |
| 379 | |
| 380 GtkWidget* confirm_hbox_; | |
| 381 | |
| 382 DISALLOW_COPY_AND_ASSIGN(ConfirmInfoBar); | |
| 383 }; | |
| 384 | |
| 385 ConfirmInfoBar::ConfirmInfoBar(ConfirmInfoBarDelegate* delegate) | |
| 386 : InfoBar(delegate) { | |
| 387 confirm_hbox_ = gtk_chrome_shrinkable_hbox_new(FALSE, FALSE, 0); | |
| 388 gtk_box_pack_start(GTK_BOX(hbox_), confirm_hbox_, TRUE, TRUE, 0); | |
| 389 gtk_widget_set_size_request(confirm_hbox_, 0, -1); | |
| 390 | |
| 391 std::string label_text = UTF16ToUTF8(delegate->GetMessageText()); | |
| 392 GtkWidget* label = gtk_label_new(label_text.c_str()); | |
| 393 gtk_util::ForceFontSizePixels(label, 13.4); | |
| 394 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
| 395 gtk_util::CenterWidgetInHBox(confirm_hbox_, label, false, kEndOfLabelSpacing); | |
| 396 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, >k_util::kGdkBlack); | |
| 397 g_signal_connect(label, "map", | |
| 398 G_CALLBACK(gtk_util::InitLabelSizeRequestAndEllipsizeMode), | |
| 399 NULL); | |
| 400 | |
| 401 AddButton(ConfirmInfoBarDelegate::BUTTON_CANCEL); | |
| 402 AddButton(ConfirmInfoBarDelegate::BUTTON_OK); | |
| 403 | |
| 404 std::string link_text = UTF16ToUTF8(delegate->GetLinkText()); | |
| 405 GtkWidget* link = gtk_chrome_link_button_new(link_text.c_str()); | |
| 406 gtk_misc_set_alignment(GTK_MISC(GTK_CHROME_LINK_BUTTON(link)->label), 0, 0.5); | |
| 407 g_signal_connect(link, "clicked", G_CALLBACK(OnLinkClickedThunk), this); | |
| 408 gtk_util::SetButtonTriggersNavigation(link); | |
| 409 // Until we switch to vector graphics, force the font size. | |
| 410 // 13.4px == 10pt @ 96dpi | |
| 411 gtk_util::ForceFontSizePixels(GTK_CHROME_LINK_BUTTON(link)->label, 13.4); | |
| 412 gtk_util::CenterWidgetInHBox(hbox_, link, true, kEndOfLabelSpacing); | |
| 413 } | |
| 414 | |
| 415 void ConfirmInfoBar::AddButton(ConfirmInfoBarDelegate::InfoBarButton type) { | |
| 416 if (delegate_->AsConfirmInfoBarDelegate()->GetButtons() & type) { | |
| 417 GtkWidget* button = gtk_button_new_with_label(UTF16ToUTF8( | |
| 418 delegate_->AsConfirmInfoBarDelegate()->GetButtonLabel(type)).c_str()); | |
| 419 gtk_util::CenterWidgetInHBox(confirm_hbox_, button, false, | |
| 420 kButtonButtonSpacing); | |
| 421 g_signal_connect(button, "clicked", | |
| 422 G_CALLBACK(type == ConfirmInfoBarDelegate::BUTTON_OK ? | |
| 423 OnOkButtonThunk : OnCancelButtonThunk), | |
| 424 this); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 void ConfirmInfoBar::OnCancelButton(GtkWidget* widget) { | |
| 429 if (delegate_->AsConfirmInfoBarDelegate()->Cancel()) | |
| 430 RemoveInfoBar(); | |
| 431 } | |
| 432 | |
| 433 void ConfirmInfoBar::OnOkButton(GtkWidget* widget) { | |
| 434 if (delegate_->AsConfirmInfoBarDelegate()->Accept()) | |
| 435 RemoveInfoBar(); | |
| 436 } | |
| 437 | |
| 438 void ConfirmInfoBar::OnLinkClicked(GtkWidget* widget) { | |
| 439 if (delegate_->AsConfirmInfoBarDelegate()->LinkClicked( | |
| 440 gtk_util::DispositionForCurrentButtonPressEvent())) { | |
| 441 RemoveInfoBar(); | |
| 442 } | |
| 443 } | |
| 444 | |
| 445 InfoBar* AlertInfoBarDelegate::CreateInfoBar() { | |
| 446 return new AlertInfoBar(this); | |
| 447 } | |
| 448 InfoBar* LinkInfoBarDelegate::CreateInfoBar() { | |
| 449 return new LinkInfoBar(this); | |
| 450 } | |
| 451 InfoBar* ConfirmInfoBarDelegate::CreateInfoBar() { | |
| 452 return new ConfirmInfoBar(this); | |
| 453 } | |
| OLD | NEW |