// 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 "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(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(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& 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::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::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 = (l10n_util::GetTextDirection() == l10n_util::LEFT_TO_RIGHT) ? 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::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(); }