| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/gtk/content_blocked_bubble_gtk.h" |
| |
| #include "app/l10n_util.h" |
| #include "base/i18n/rtl.h" |
| #include "chrome/browser/blocked_popup_container.h" |
| #include "chrome/browser/content_setting_bubble_model.h" |
| #include "chrome/browser/gtk/gtk_chrome_link_button.h" |
| #include "chrome/browser/gtk/gtk_theme_provider.h" |
| #include "chrome/browser/gtk/gtk_util.h" |
| #include "chrome/browser/gtk/options/content_settings_window_gtk.h" |
| #include "chrome/browser/host_content_settings_map.h" |
| #include "chrome/browser/profile.h" |
| #include "chrome/browser/tab_contents/tab_contents.h" |
| #include "chrome/common/content_settings.h" |
| #include "chrome/common/notification_service.h" |
| #include "chrome/common/notification_type.h" |
| #include "gfx/gtk_util.h" |
| #include "grit/app_resources.h" |
| #include "grit/generated_resources.h" |
| |
| // Padding between content and edge of info bubble. |
| static const int kContentBorder = 7; |
| |
| ContentSettingBubbleGtk::ContentSettingBubbleGtk( |
| GtkWindow* toplevel_window, |
| const gfx::Rect& bounds, |
| InfoBubbleGtkDelegate* delegate, |
| ContentSettingBubbleModel* content_setting_bubble_model, |
| Profile* profile, |
| TabContents* tab_contents) |
| : toplevel_window_(toplevel_window), |
| bounds_(bounds), |
| profile_(profile), |
| tab_contents_(tab_contents), |
| delegate_(delegate), |
| content_setting_bubble_model_(content_setting_bubble_model), |
| info_bubble_(NULL) { |
| registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, |
| Source<TabContents>(tab_contents)); |
| BuildBubble(); |
| } |
| |
| ContentSettingBubbleGtk::~ContentSettingBubbleGtk() { |
| } |
| |
| void ContentSettingBubbleGtk::Close() { |
| if (info_bubble_) |
| info_bubble_->Close(); |
| } |
| |
| void ContentSettingBubbleGtk::InfoBubbleClosing(InfoBubbleGtk* info_bubble, |
| bool closed_by_escape) { |
| delegate_->InfoBubbleClosing(info_bubble, closed_by_escape); |
| delete this; |
| } |
| |
| void ContentSettingBubbleGtk::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); |
| DCHECK(source == Source<TabContents>(tab_contents_)); |
| tab_contents_ = NULL; |
| } |
| |
| void ContentSettingBubbleGtk::BuildBubble() { |
| GtkThemeProvider* theme_provider = GtkThemeProvider::GetFrom(profile_); |
| |
| GtkWidget* bubble_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); |
| gtk_container_set_border_width(GTK_CONTAINER(bubble_content), kContentBorder); |
| |
| // Add the content label. |
| GtkWidget* label = gtk_label_new( |
| content_setting_bubble_model_->bubble_content().title.c_str()); |
| gtk_box_pack_start(GTK_BOX(bubble_content), label, FALSE, FALSE, 0); |
| |
| if (content_setting_bubble_model_->content_type() == |
| CONTENT_SETTINGS_TYPE_POPUPS) { |
| const std::vector<ContentSettingBubbleModel::PopupItem>& popup_items = |
| content_setting_bubble_model_->bubble_content().popup_items; |
| GtkWidget* table = gtk_table_new(popup_items.size(), 2, FALSE); |
| int row = 0; |
| for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator |
| i(popup_items.begin()); i != popup_items.end(); ++i, ++row) { |
| GtkWidget* image = gtk_image_new(); |
| if (!i->bitmap.empty()) { |
| GdkPixbuf* icon_pixbuf = gfx::GdkPixbufFromSkBitmap(&i->bitmap); |
| gtk_image_set_from_pixbuf(GTK_IMAGE(image), icon_pixbuf); |
| g_object_unref(icon_pixbuf); |
| |
| // We stuff the image in an event box so we can trap mouse clicks on the |
| // image (and launch the popup). |
| GtkWidget* event_box = gtk_event_box_new(); |
| gtk_container_add(GTK_CONTAINER(event_box), image); |
| |
| popup_icons_[event_box] = i -popup_items.begin(); |
| g_signal_connect(event_box, "button_press_event", |
| G_CALLBACK(OnPopupIconButtonPress), this); |
| gtk_table_attach(GTK_TABLE(table), event_box, 0, 1, row, row + 1, |
| GTK_FILL, GTK_FILL, gtk_util::kControlSpacing / 2, |
| gtk_util::kControlSpacing / 2); |
| } |
| |
| GtkWidget* button = gtk_chrome_link_button_new(i->title.c_str()); |
| popup_links_[button] = i -popup_items.begin(); |
| g_signal_connect(button, "clicked", G_CALLBACK(OnPopupLinkClicked), |
| this); |
| gtk_table_attach(GTK_TABLE(table), button, 1, 2, row, row + 1, |
| GTK_FILL, GTK_FILL, gtk_util::kControlSpacing / 2, |
| gtk_util::kControlSpacing / 2); |
| } |
| |
| gtk_box_pack_start(GTK_BOX(bubble_content), table, FALSE, FALSE, 0); |
| } |
| |
| if (content_setting_bubble_model_->content_type() != |
| CONTENT_SETTINGS_TYPE_COOKIES) { |
| const ContentSettingBubbleModel::RadioGroups& radio_groups = |
| content_setting_bubble_model_->bubble_content().radio_groups; |
| for (ContentSettingBubbleModel::RadioGroups::const_iterator i = |
| radio_groups.begin(); i != radio_groups.end(); ++i) { |
| const ContentSettingBubbleModel::RadioItems& radio_items = i->radio_items; |
| RadioGroupGtk radio_group_gtk; |
| for (ContentSettingBubbleModel::RadioItems::const_iterator j = |
| radio_items.begin(); j != radio_items.end(); ++j) { |
| GtkWidget* radio = |
| radio_group_gtk.empty() ? |
| gtk_radio_button_new_with_label(NULL, j->c_str()) : |
| gtk_radio_button_new_with_label_from_widget( |
| GTK_RADIO_BUTTON(radio_group_gtk[0]), |
| j->c_str()); |
| gtk_box_pack_start(GTK_BOX(bubble_content), radio, FALSE, FALSE, 0); |
| if (j - radio_items.begin() == i->default_item) { |
| // We must set the default value before we attach the signal handlers |
| // or pain occurs. |
| gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE); |
| } |
| radio_group_gtk.push_back(radio); |
| } |
| for (std::vector<GtkWidget*>::const_iterator j = radio_group_gtk.begin(); |
| j != radio_group_gtk.end(); ++j) { |
| g_signal_connect(*j, "toggled", G_CALLBACK(OnRadioToggled), this); |
| } |
| radio_groups_gtk_.push_back(radio_group_gtk); |
| gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(), FALSE, |
| FALSE, 0); |
| } |
| } |
| |
| GtkWidget* bottom_box = gtk_hbox_new(FALSE, 0); |
| |
| GtkWidget* manage_link = gtk_chrome_link_button_new( |
| content_setting_bubble_model_->bubble_content().manage_link.c_str()); |
| g_signal_connect(manage_link, "clicked", G_CALLBACK(OnManageLinkClicked), |
| this); |
| gtk_box_pack_start(GTK_BOX(bottom_box), manage_link, FALSE, FALSE, 0); |
| |
| GtkWidget* button = gtk_button_new_with_label( |
| l10n_util::GetStringUTF8(IDS_DONE).c_str()); |
| g_signal_connect(button, "clicked", G_CALLBACK(OnCloseButtonClicked), this); |
| gtk_box_pack_end(GTK_BOX(bottom_box), button, FALSE, FALSE, 0); |
| |
| gtk_box_pack_start(GTK_BOX(bubble_content), bottom_box, FALSE, FALSE, 0); |
| |
| InfoBubbleGtk::ArrowLocationGtk arrow_location = |
| !base::i18n::IsRTL() ? |
| InfoBubbleGtk::ARROW_LOCATION_TOP_RIGHT : |
| InfoBubbleGtk::ARROW_LOCATION_TOP_LEFT; |
| info_bubble_ = InfoBubbleGtk::Show( |
| toplevel_window_, |
| bounds_, |
| bubble_content, |
| arrow_location, |
| true, |
| theme_provider, |
| this); |
| } |
| |
| // static |
| void ContentSettingBubbleGtk::OnPopupIconButtonPress( |
| GtkWidget* icon_event_box, |
| GdkEventButton* event, |
| ContentSettingBubbleGtk* bubble) { |
| PopupMap::iterator i(bubble->popup_icons_.find(icon_event_box)); |
| DCHECK(i != bubble->popup_icons_.end()); |
| bubble->content_setting_bubble_model_->OnPopupClicked(i->second); |
| // The views interface implicitly closes because of the launching of a new |
| // window; we need to do that explicitly. |
| bubble->Close(); |
| |
| } |
| |
| // static |
| void ContentSettingBubbleGtk::OnPopupLinkClicked( |
| GtkWidget* button, |
| ContentSettingBubbleGtk* bubble) { |
| PopupMap::iterator i(bubble->popup_links_.find(button)); |
| DCHECK(i != bubble->popup_links_.end()); |
| bubble->content_setting_bubble_model_->OnPopupClicked(i->second); |
| // The views interface implicitly closes because of the launching of a new |
| // window; we need to do that explicitly. |
| bubble->Close(); |
| } |
| |
| // static |
| void ContentSettingBubbleGtk::OnRadioToggled( |
| GtkWidget* widget, |
| ContentSettingBubbleGtk* bubble) { |
| for (std::vector<RadioGroupGtk>::const_iterator i = |
| bubble->radio_groups_gtk_.begin(); |
| i != bubble->radio_groups_gtk_.end(); ++i) { |
| for (RadioGroupGtk::const_iterator j = i->begin(); j != i->end(); j++) { |
| if (widget == *j) { |
| bubble->content_setting_bubble_model_->OnRadioClicked( |
| i - bubble->radio_groups_gtk_.begin(), |
| j - i->begin()); |
| return; |
| } |
| } |
| } |
| NOTREACHED() << "unknown radio toggled"; |
| } |
| |
| // static |
| void ContentSettingBubbleGtk::OnCloseButtonClicked( |
| GtkButton *button, |
| ContentSettingBubbleGtk* bubble) { |
| bubble->Close(); |
| } |
| |
| // static |
| void ContentSettingBubbleGtk::OnManageLinkClicked( |
| GtkButton* button, |
| ContentSettingBubbleGtk* bubble) { |
| bubble->content_setting_bubble_model_->OnManageLinkClicked(); |
| bubble->Close(); |
| } |