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 "chrome/browser/ui/gtk/about_chrome_dialog.h"
      6 
      7 #include <gtk/gtk.h>
      8 
      9 #include <string>
     10 #include <vector>
     11 
     12 #include "base/utf_string_conversions.h"
     13 #include "chrome/browser/google/google_util.h"
     14 #include "chrome/browser/platform_util.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/ui/browser_list.h"
     17 #include "chrome/browser/ui/gtk/cairo_cached_surface.h"
     18 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
     19 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     20 #include "chrome/browser/ui/gtk/gtk_util.h"
     21 #include "chrome/common/chrome_constants.h"
     22 #include "chrome/common/chrome_version_info.h"
     23 #include "chrome/common/url_constants.h"
     24 #include "grit/chromium_strings.h"
     25 #include "grit/generated_resources.h"
     26 #include "grit/locale_settings.h"
     27 #include "grit/theme_resources.h"
     28 #include "ui/base/l10n/l10n_util.h"
     29 #include "ui/base/resource/resource_bundle.h"
     30 #include "webkit/glue/webkit_glue.h"
     31 
     32 namespace {
     33 
     34 // Left or right margin.
     35 const int kPanelHorizMargin = 13;
     36 
     37 // Top or bottom margin.
     38 const int kPanelVertMargin = 20;
     39 
     40 // Extra spacing between product name and version number.
     41 const int kExtraLineSpacing = 5;
     42 
     43 // These are used as placeholder text around the links in the text in the about
     44 // dialog.
     45 const char* kBeginLinkChr = "BEGIN_LINK_CHR";
     46 const char* kBeginLinkOss = "BEGIN_LINK_OSS";
     47 // We don't actually use this one.
     48 // const char* kEndLinkChr = "END_LINK_CHR";
     49 const char* kEndLinkOss = "END_LINK_OSS";
     50 const char* kBeginLink = "BEGIN_LINK";
     51 const char* kEndLink = "END_LINK";
     52 
     53 void OnResponse(GtkWidget* dialog, int response_id) {
     54   // We're done.
     55   gtk_widget_destroy(dialog);
     56 }
     57 
     58 GtkWidget* MakeMarkupLabel(const char* format, const std::string& str) {
     59   GtkWidget* label = gtk_label_new(NULL);
     60   char* markup = g_markup_printf_escaped(format, str.c_str());
     61   gtk_label_set_markup(GTK_LABEL(label), markup);
     62   g_free(markup);
     63 
     64   // Left align it.
     65   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
     66 
     67   return label;
     68 }
     69 
     70 void OnLinkButtonClick(GtkWidget* button, const char* url) {
     71   BrowserList::GetLastActive()->
     72       OpenURL(GURL(url), GURL(), NEW_WINDOW, PageTransition::LINK);
     73 }
     74 
     75 const char* GetChromiumUrl() {
     76   static GURL url = google_util::AppendGoogleLocaleParam(
     77       GURL(chrome::kChromiumProjectURL));
     78   return url.spec().c_str();
     79 }
     80 
     81 gboolean OnEventBoxExpose(GtkWidget* event_box,
     82                           GdkEventExpose* expose,
     83                           gboolean user_data) {
     84   cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(event_box->window));
     85   gdk_cairo_rectangle(cr, &expose->area);
     86   cairo_clip(cr);
     87   GtkThemeService* theme_provider =
     88       GtkThemeService::GetFrom(BrowserList::GetLastActive()->profile());
     89   CairoCachedSurface* background = theme_provider->GetSurfaceNamed(
     90       IDR_ABOUT_BACKGROUND_COLOR, event_box);
     91 
     92   background->SetSource(cr, 0, 0);
     93   cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
     94   gdk_cairo_rectangle(cr, &expose->area);
     95   cairo_fill(cr);
     96   cairo_destroy(cr);
     97   return FALSE;
     98 }
     99 
    100 }  // namespace
    101 
    102 void ShowAboutDialogForProfile(GtkWindow* parent, Profile* profile) {
    103   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    104   static GdkPixbuf* background = rb.GetPixbufNamed(IDR_ABOUT_BACKGROUND);
    105   chrome::VersionInfo version_info;
    106   std::string current_version = version_info.Version();
    107 #if !defined(GOOGLE_CHROME_BUILD)
    108   current_version += " (";
    109   current_version += version_info.LastChange();
    110   current_version += ")";
    111 #endif
    112   std::string channel = platform_util::GetVersionStringModifier();
    113   if (!channel.empty())
    114     current_version += " " + channel;
    115 
    116   // Build the dialog.
    117   GtkWidget* dialog = gtk_dialog_new_with_buttons(
    118       l10n_util::GetStringUTF8(IDS_ABOUT_CHROME_TITLE).c_str(),
    119       parent,
    120       GTK_DIALOG_MODAL,
    121       NULL);
    122   // Pick up the style set in gtk_util.cc:InitRCStyles().
    123   // The layout of this dialog is special because the logo should be flush
    124   // with the edges of the window.
    125   gtk_widget_set_name(dialog, "about-dialog");
    126   gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
    127 
    128   GtkWidget* close_button = gtk_dialog_add_button(GTK_DIALOG(dialog),
    129       GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
    130 
    131   GtkWidget* content_area = GTK_DIALOG(dialog)->vbox;
    132 
    133   // Use an event box to get the background painting correctly
    134   GtkWidget* ebox = gtk_event_box_new();
    135   gtk_widget_set_app_paintable(ebox, TRUE);
    136   g_signal_connect(ebox, "expose-event", G_CALLBACK(OnEventBoxExpose), NULL);
    137 
    138   GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
    139 
    140   GtkWidget* text_alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
    141   gtk_alignment_set_padding(GTK_ALIGNMENT(text_alignment),
    142                             kPanelVertMargin, kPanelVertMargin,
    143                             kPanelHorizMargin, kPanelHorizMargin);
    144 
    145   GtkWidget* text_vbox = gtk_vbox_new(FALSE, kExtraLineSpacing);
    146 
    147   GdkColor black = gtk_util::kGdkBlack;
    148   GtkWidget* product_label = MakeMarkupLabel(
    149       "<span font_desc=\"18\" style=\"normal\">%s</span>",
    150       l10n_util::GetStringUTF8(IDS_PRODUCT_NAME));
    151   gtk_widget_modify_fg(product_label, GTK_STATE_NORMAL, &black);
    152   gtk_box_pack_start(GTK_BOX(text_vbox), product_label, FALSE, FALSE, 0);
    153 
    154   GtkWidget* version_label = gtk_label_new(current_version.c_str());
    155   gtk_misc_set_alignment(GTK_MISC(version_label), 0.0, 0.5);
    156   gtk_label_set_selectable(GTK_LABEL(version_label), TRUE);
    157   gtk_widget_modify_fg(version_label, GTK_STATE_NORMAL, &black);
    158   gtk_box_pack_start(GTK_BOX(text_vbox), version_label, FALSE, FALSE, 0);
    159 
    160   gtk_container_add(GTK_CONTAINER(text_alignment), text_vbox);
    161   gtk_box_pack_start(GTK_BOX(hbox), text_alignment, TRUE, TRUE, 0);
    162 
    163   GtkWidget* image_vbox = gtk_vbox_new(FALSE, 0);
    164   gtk_box_pack_end(GTK_BOX(image_vbox),
    165                    gtk_image_new_from_pixbuf(background),
    166                    FALSE, FALSE, 0);
    167 
    168   gtk_box_pack_start(GTK_BOX(hbox), image_vbox, FALSE, FALSE, 0);
    169   gtk_container_add(GTK_CONTAINER(ebox), hbox);
    170   gtk_box_pack_start(GTK_BOX(content_area), ebox, TRUE, TRUE, 0);
    171 
    172   // We use a separate box for the licensing etc. text.  See the comment near
    173   // the top of this function about using a special layout for this dialog.
    174   GtkWidget* vbox = gtk_vbox_new(FALSE, 0);
    175 
    176   GtkWidget* copyright_label = gtk_label_new(
    177       l10n_util::GetStringUTF8(IDS_ABOUT_VERSION_COPYRIGHT).c_str());
    178   gtk_misc_set_alignment(GTK_MISC(copyright_label), 0.0, 0.5);
    179   gtk_box_pack_start(GTK_BOX(vbox), copyright_label, FALSE, FALSE, 5);
    180 
    181   std::string license = l10n_util::GetStringUTF8(IDS_ABOUT_VERSION_LICENSE);
    182   bool chromium_url_appears_first =
    183       license.find(kBeginLinkChr) < license.find(kBeginLinkOss);
    184   size_t link1 = license.find(kBeginLink);
    185   DCHECK(link1 != std::string::npos);
    186   size_t link1_end = license.find(kEndLink, link1);
    187   DCHECK(link1_end != std::string::npos);
    188   size_t link2 = license.find(kBeginLink, link1_end);
    189   DCHECK(link2 != std::string::npos);
    190   size_t link2_end = license.find(kEndLink, link2);
    191   DCHECK(link1_end != std::string::npos);
    192 
    193   GtkWidget* license_chunk1 = gtk_label_new(license.substr(0, link1).c_str());
    194   gtk_misc_set_alignment(GTK_MISC(license_chunk1), 0.0, 0.5);
    195   GtkWidget* license_chunk2 = gtk_label_new(
    196       license.substr(link1_end + strlen(kEndLinkOss),
    197                      link2 - link1_end - strlen(kEndLinkOss)).c_str());
    198   gtk_misc_set_alignment(GTK_MISC(license_chunk2), 0.0, 0.5);
    199   GtkWidget* license_chunk3 = gtk_label_new(
    200       license.substr(link2_end + strlen(kEndLinkOss)).c_str());
    201   gtk_misc_set_alignment(GTK_MISC(license_chunk3), 0.0, 0.5);
    202 
    203   std::string first_link_text =
    204       license.substr(link1 + strlen(kBeginLinkOss),
    205                      link1_end - link1 - strlen(kBeginLinkOss));
    206   std::string second_link_text =
    207       license.substr(link2 + strlen(kBeginLinkOss),
    208                      link2_end - link2 - strlen(kBeginLinkOss));
    209 
    210   GtkWidget* first_link = gtk_chrome_link_button_new(first_link_text.c_str());
    211   GtkWidget* second_link = gtk_chrome_link_button_new(second_link_text.c_str());
    212   if (!chromium_url_appears_first) {
    213     GtkWidget* swap = second_link;
    214     second_link = first_link;
    215     first_link = swap;
    216   }
    217 
    218   g_signal_connect(chromium_url_appears_first ? first_link : second_link,
    219                    "clicked", G_CALLBACK(OnLinkButtonClick),
    220                    const_cast<char*>(GetChromiumUrl()));
    221   g_signal_connect(chromium_url_appears_first ? second_link : first_link,
    222                    "clicked", G_CALLBACK(OnLinkButtonClick),
    223                    const_cast<char*>(chrome::kAboutCreditsURL));
    224 
    225   GtkWidget* license_hbox = gtk_hbox_new(FALSE, 0);
    226   gtk_box_pack_start(GTK_BOX(license_hbox), license_chunk1,
    227                      FALSE, FALSE, 0);
    228   gtk_box_pack_start(GTK_BOX(license_hbox), first_link,
    229                      FALSE, FALSE, 0);
    230   gtk_box_pack_start(GTK_BOX(license_hbox), license_chunk2,
    231                      FALSE, FALSE, 0);
    232 
    233   // Since there's no good way to dynamically wrap the license block, force
    234   // a line break right before the second link (which matches en-US Windows
    235   // chromium).
    236   GtkWidget* license_hbox2 = gtk_hbox_new(FALSE, 0);
    237   gtk_box_pack_start(GTK_BOX(license_hbox2), second_link,
    238                      FALSE, FALSE, 0);
    239   gtk_box_pack_start(GTK_BOX(license_hbox2), license_chunk3,
    240                      FALSE, FALSE, 0);
    241 
    242   GtkWidget* license_vbox = gtk_vbox_new(FALSE, 0);
    243   gtk_box_pack_start(GTK_BOX(license_vbox), license_hbox, FALSE, FALSE, 0);
    244   gtk_box_pack_start(GTK_BOX(license_vbox), license_hbox2, FALSE, FALSE, 0);
    245   gtk_box_pack_start(GTK_BOX(vbox), license_vbox, FALSE, FALSE, 0);
    246 
    247 #if defined(GOOGLE_CHROME_BUILD)
    248   // Spacing line.
    249   gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(""), FALSE, FALSE, 0);
    250 
    251   std::vector<size_t> url_offsets;
    252   string16 text = l10n_util::GetStringFUTF16(IDS_ABOUT_TERMS_OF_SERVICE,
    253                                              string16(),
    254                                              string16(),
    255                                              &url_offsets);
    256 
    257   GtkWidget* tos_chunk1 = gtk_label_new(
    258       UTF16ToUTF8(text.substr(0, url_offsets[0])).c_str());
    259   gtk_misc_set_alignment(GTK_MISC(tos_chunk1), 0.0, 0.5);
    260   GtkWidget* tos_link = gtk_chrome_link_button_new(
    261       l10n_util::GetStringUTF8(IDS_TERMS_OF_SERVICE).c_str());
    262   GtkWidget* tos_chunk2 = gtk_label_new(
    263       UTF16ToUTF8(text.substr(url_offsets[0])).c_str());
    264   gtk_misc_set_alignment(GTK_MISC(tos_chunk2), 0.0, 0.5);
    265 
    266   GtkWidget* tos_hbox = gtk_hbox_new(FALSE, 0);
    267   gtk_box_pack_start(GTK_BOX(tos_hbox), tos_chunk1, FALSE, FALSE, 0);
    268   gtk_box_pack_start(GTK_BOX(tos_hbox), tos_link, FALSE, FALSE, 0);
    269   gtk_box_pack_start(GTK_BOX(tos_hbox), tos_chunk2, FALSE, FALSE, 0);
    270 
    271   g_signal_connect(tos_link, "clicked", G_CALLBACK(OnLinkButtonClick),
    272     const_cast<char*>(chrome::kAboutTermsURL));
    273   gtk_box_pack_start(GTK_BOX(vbox), tos_hbox, TRUE, TRUE, 0);
    274 #endif
    275 
    276   GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
    277   gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
    278       gtk_util::kContentAreaBorder, 0,
    279       gtk_util::kContentAreaBorder, gtk_util::kContentAreaBorder);
    280   gtk_container_add(GTK_CONTAINER(alignment), vbox);
    281   gtk_box_pack_start(GTK_BOX(content_area), alignment, FALSE, FALSE, 0);
    282 
    283   g_signal_connect(dialog, "response", G_CALLBACK(OnResponse), NULL);
    284   gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
    285   gtk_widget_show_all(dialog);
    286   gtk_widget_grab_focus(close_button);
    287 }
    288