Home | History | Annotate | Download | only in gtk
      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 <gtk/gtk.h>
      6 
      7 #include "build/build_config.h"
      8 
      9 #include "base/i18n/rtl.h"
     10 #include "base/utf_string_conversions.h"
     11 #include "chrome/browser/google/google_util.h"
     12 #include "chrome/browser/page_info_model.h"
     13 #include "chrome/browser/page_info_window.h"
     14 #include "chrome/browser/ui/browser_list.h"
     15 #include "chrome/browser/ui/gtk/browser_toolbar_gtk.h"
     16 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
     17 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
     18 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     19 #include "chrome/browser/ui/gtk/gtk_util.h"
     20 #include "chrome/browser/ui/gtk/info_bubble_gtk.h"
     21 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
     22 #include "chrome/common/url_constants.h"
     23 #include "content/browser/certificate_viewer.h"
     24 #include "content/common/notification_observer.h"
     25 #include "content/common/notification_registrar.h"
     26 #include "content/common/notification_service.h"
     27 #include "googleurl/src/gurl.h"
     28 #include "grit/generated_resources.h"
     29 #include "grit/locale_settings.h"
     30 #include "ui/base/l10n/l10n_util.h"
     31 
     32 class Profile;
     33 
     34 namespace {
     35 
     36 class PageInfoBubbleGtk : public PageInfoModel::PageInfoModelObserver,
     37                           public InfoBubbleGtkDelegate,
     38                           public NotificationObserver {
     39  public:
     40   PageInfoBubbleGtk(gfx::NativeWindow parent,
     41                     Profile* profile,
     42                     const GURL& url,
     43                     const NavigationEntry::SSLStatus& ssl,
     44                     bool show_history);
     45   virtual ~PageInfoBubbleGtk();
     46 
     47   // PageInfoModelObserver implementation:
     48   virtual void ModelChanged();
     49 
     50   // NotificationObserver implementation:
     51   virtual void Observe(NotificationType type,
     52                        const NotificationSource& source,
     53                        const NotificationDetails& details);
     54 
     55   // InfoBubbleGtkDelegate implementation:
     56   virtual void InfoBubbleClosing(InfoBubbleGtk* info_bubble,
     57                                  bool closed_by_escape);
     58 
     59  private:
     60   // Layouts the different sections retrieved from the model.
     61   void InitContents();
     62 
     63   // Returns a widget that contains the UI for the passed |section|.
     64   GtkWidget* CreateSection(const PageInfoModel::SectionInfo& section);
     65 
     66   // Link button callbacks.
     67   CHROMEGTK_CALLBACK_0(PageInfoBubbleGtk, void, OnViewCertLinkClicked);
     68   CHROMEGTK_CALLBACK_0(PageInfoBubbleGtk, void, OnHelpLinkClicked);
     69 
     70   // The model containing the different sections to display.
     71   PageInfoModel model_;
     72 
     73   // The url for this dialog. Should be unique among active dialogs.
     74   GURL url_;
     75 
     76   // The id of the certificate for this page.
     77   int cert_id_;
     78 
     79   // Parent window.
     80   GtkWindow* parent_;
     81 
     82   // The virtual box containing the sections.
     83   GtkWidget* contents_;
     84 
     85   // The widget relative to which we are positioned.
     86   GtkWidget* anchor_;
     87 
     88   // Provides colors and stuff.
     89   GtkThemeService* theme_service_;
     90 
     91   // The various elements in the interface we keep track of for theme changes.
     92   std::vector<GtkWidget*> labels_;
     93   std::vector<GtkWidget*> links_;
     94 
     95   InfoBubbleGtk* bubble_;
     96 
     97   NotificationRegistrar registrar_;
     98 
     99   DISALLOW_COPY_AND_ASSIGN(PageInfoBubbleGtk);
    100 };
    101 
    102 PageInfoBubbleGtk::PageInfoBubbleGtk(gfx::NativeWindow parent,
    103                                      Profile* profile,
    104                                      const GURL& url,
    105                                      const NavigationEntry::SSLStatus& ssl,
    106                                      bool show_history)
    107     : ALLOW_THIS_IN_INITIALIZER_LIST(model_(profile, url, ssl,
    108                                             show_history, this)),
    109       url_(url),
    110       cert_id_(ssl.cert_id()),
    111       parent_(parent),
    112       contents_(NULL),
    113       theme_service_(GtkThemeService::GetFrom(profile)) {
    114   BrowserWindowGtk* browser_window =
    115       BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent);
    116 
    117   anchor_ = browser_window->
    118       GetToolbar()->GetLocationBarView()->location_icon_widget();
    119 
    120   registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
    121                  NotificationService::AllSources());
    122 
    123   InitContents();
    124 
    125   InfoBubbleGtk::ArrowLocationGtk arrow_location = base::i18n::IsRTL() ?
    126       InfoBubbleGtk::ARROW_LOCATION_TOP_RIGHT :
    127       InfoBubbleGtk::ARROW_LOCATION_TOP_LEFT;
    128   bubble_ = InfoBubbleGtk::Show(anchor_,
    129                                 NULL,  // |rect|
    130                                 contents_,
    131                                 arrow_location,
    132                                 true,  // |match_system_theme|
    133                                 true,  // |grab_input|
    134                                 theme_service_,
    135                                 this);  // |delegate|
    136   if (!bubble_) {
    137     NOTREACHED();
    138     return;
    139   }
    140 }
    141 
    142 PageInfoBubbleGtk::~PageInfoBubbleGtk() {
    143 }
    144 
    145 void PageInfoBubbleGtk::ModelChanged() {
    146   InitContents();
    147 }
    148 
    149 void PageInfoBubbleGtk::Observe(NotificationType type,
    150                                 const NotificationSource& source,
    151                                 const NotificationDetails& details) {
    152   DCHECK(type == NotificationType::BROWSER_THEME_CHANGED);
    153 
    154   for (std::vector<GtkWidget*>::iterator it = links_.begin();
    155        it != links_.end(); ++it) {
    156     gtk_chrome_link_button_set_use_gtk_theme(
    157         GTK_CHROME_LINK_BUTTON(*it),
    158         theme_service_->UseGtkTheme());
    159   }
    160 
    161   if (theme_service_->UseGtkTheme()) {
    162     for (std::vector<GtkWidget*>::iterator it = labels_.begin();
    163          it != labels_.end(); ++it) {
    164       gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, NULL);
    165     }
    166   } else {
    167     for (std::vector<GtkWidget*>::iterator it = labels_.begin();
    168          it != labels_.end(); ++it) {
    169       gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, &gtk_util::kGdkBlack);
    170     }
    171   }
    172 }
    173 
    174 void PageInfoBubbleGtk::InfoBubbleClosing(InfoBubbleGtk* info_bubble,
    175                                           bool closed_by_escape) {
    176   delete this;
    177 }
    178 
    179 void PageInfoBubbleGtk::InitContents() {
    180   if (!contents_) {
    181     contents_ = gtk_vbox_new(FALSE, gtk_util::kContentAreaSpacing);
    182     gtk_container_set_border_width(GTK_CONTAINER(contents_),
    183                                    gtk_util::kContentAreaBorder);
    184   } else {
    185     labels_.clear();
    186     links_.clear();
    187     gtk_util::RemoveAllChildren(contents_);
    188   }
    189 
    190   for (int i = 0; i < model_.GetSectionCount(); i++) {
    191     gtk_box_pack_start(GTK_BOX(contents_),
    192                        CreateSection(model_.GetSectionInfo(i)),
    193                        FALSE, FALSE, 0);
    194     gtk_box_pack_start(GTK_BOX(contents_),
    195                        gtk_hseparator_new(),
    196                        FALSE, FALSE, 0);
    197   }
    198 
    199   GtkWidget* help_link = gtk_chrome_link_button_new(
    200       l10n_util::GetStringUTF8(IDS_PAGE_INFO_HELP_CENTER_LINK).c_str());
    201   links_.push_back(help_link);
    202   GtkWidget* help_link_hbox = gtk_hbox_new(FALSE, 0);
    203   // Stick it in an hbox so it doesn't expand to the whole width.
    204   gtk_box_pack_start(GTK_BOX(help_link_hbox), help_link, FALSE, FALSE, 0);
    205   gtk_box_pack_start(GTK_BOX(contents_), help_link_hbox, FALSE, FALSE, 0);
    206   g_signal_connect(help_link, "clicked",
    207                    G_CALLBACK(OnHelpLinkClickedThunk), this);
    208 
    209   theme_service_->InitThemesFor(this);
    210   gtk_widget_show_all(contents_);
    211 }
    212 
    213 GtkWidget* PageInfoBubbleGtk::CreateSection(
    214     const PageInfoModel::SectionInfo& section) {
    215   GtkWidget* section_box = gtk_hbox_new(FALSE, gtk_util::kControlSpacing);
    216 
    217   GdkPixbuf* pixbuf = *model_.GetIconImage(section.icon_id);
    218   if (pixbuf) {
    219     GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf);
    220     gtk_box_pack_start(GTK_BOX(section_box), image, FALSE, FALSE, 0);
    221     gtk_misc_set_alignment(GTK_MISC(image), 0, 0);
    222   }
    223 
    224   GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
    225   gtk_box_pack_start(GTK_BOX(section_box), vbox, TRUE, TRUE, 0);
    226 
    227   if (!section.headline.empty()) {
    228     GtkWidget* label = gtk_label_new(UTF16ToUTF8(section.headline).c_str());
    229     gtk_label_set_selectable(GTK_LABEL(label), TRUE);
    230     labels_.push_back(label);
    231     PangoAttrList* attributes = pango_attr_list_new();
    232     pango_attr_list_insert(attributes,
    233                            pango_attr_weight_new(PANGO_WEIGHT_BOLD));
    234     gtk_label_set_attributes(GTK_LABEL(label), attributes);
    235     pango_attr_list_unref(attributes);
    236     gtk_util::SetLabelWidth(label, 400);
    237     // Allow linebreaking in the middle of words if necessary, so that extremely
    238     // long hostnames (longer than one line) will still be completely shown.
    239     gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD_CHAR);
    240     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
    241   }
    242   GtkWidget* label = gtk_label_new(UTF16ToUTF8(section.description).c_str());
    243   gtk_label_set_selectable(GTK_LABEL(label), TRUE);
    244   labels_.push_back(label);
    245   gtk_util::SetLabelWidth(label, 400);
    246   // Allow linebreaking in the middle of words if necessary, so that extremely
    247   // long hostnames (longer than one line) will still be completely shown.
    248   gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD_CHAR);
    249   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
    250 
    251   if (section.type == PageInfoModel::SECTION_INFO_IDENTITY && cert_id_ > 0) {
    252     GtkWidget* view_cert_link = gtk_chrome_link_button_new(
    253         l10n_util::GetStringUTF8(IDS_PAGEINFO_CERT_INFO_BUTTON).c_str());
    254     links_.push_back(view_cert_link);
    255     GtkWidget* cert_link_hbox = gtk_hbox_new(FALSE, 0);
    256     // Stick it in an hbox so it doesn't expand to the whole width.
    257     gtk_box_pack_start(GTK_BOX(cert_link_hbox), view_cert_link,
    258                        FALSE, FALSE, 0);
    259     gtk_box_pack_start(GTK_BOX(vbox), cert_link_hbox, FALSE, FALSE, 0);
    260     g_signal_connect(view_cert_link, "clicked",
    261                      G_CALLBACK(OnViewCertLinkClickedThunk), this);
    262   }
    263 
    264   return section_box;
    265 }
    266 
    267 void PageInfoBubbleGtk::OnViewCertLinkClicked(GtkWidget* widget) {
    268   ShowCertificateViewerByID(GTK_WINDOW(parent_), cert_id_);
    269   bubble_->Close();
    270 }
    271 
    272 void PageInfoBubbleGtk::OnHelpLinkClicked(GtkWidget* widget) {
    273   GURL url = google_util::AppendGoogleLocaleParam(
    274       GURL(chrome::kPageInfoHelpCenterURL));
    275   Browser* browser = BrowserList::GetLastActive();
    276   browser->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
    277   bubble_->Close();
    278 }
    279 
    280 }  // namespace
    281 
    282 namespace browser {
    283 
    284 void ShowPageInfoBubble(gfx::NativeWindow parent,
    285                         Profile* profile,
    286                         const GURL& url,
    287                         const NavigationEntry::SSLStatus& ssl,
    288                         bool show_history) {
    289   new PageInfoBubbleGtk(parent, profile, url, ssl, show_history);
    290 }
    291 
    292 }  // namespace browser
    293