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/first_run_bubble.h"
      6 
      7 #include <gtk/gtk.h>
      8 
      9 #include "base/command_line.h"
     10 #include "base/i18n/rtl.h"
     11 #include "base/utf_string_conversions.h"
     12 #include "chrome/browser/search_engines/util.h"
     13 #include "chrome/browser/ui/browser.h"
     14 #include "chrome/browser/ui/browser_list.h"
     15 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     16 #include "chrome/browser/ui/gtk/gtk_util.h"
     17 #include "content/common/notification_service.h"
     18 #include "grit/chromium_strings.h"
     19 #include "grit/generated_resources.h"
     20 #include "grit/locale_settings.h"
     21 #include "ui/base/l10n/l10n_util.h"
     22 
     23 namespace {
     24 // Markup for the text of the Omnibox search label
     25 const char kSearchLabelMarkup[] = "<big><b>%s</b></big>";
     26 
     27 // Padding for the buttons on first run bubble.
     28 const int kButtonPadding = 4;
     29 
     30 // Padding between content and edge of info bubble.
     31 const int kContentBorder = 7;
     32 
     33 // Vertical spacing between labels.
     34 const int kInterLineSpacing = 5;
     35 
     36 }  // namespace
     37 
     38 // static
     39 void FirstRunBubble::Show(Profile* profile,
     40                           GtkWidget* anchor,
     41                           const gfx::Rect& rect,
     42                           FirstRun::BubbleType bubble_type) {
     43   new FirstRunBubble(profile, anchor, rect, bubble_type);
     44 }
     45 
     46 void FirstRunBubble::InfoBubbleClosing(InfoBubbleGtk* info_bubble,
     47                                        bool closed_by_escape) {
     48   // TODO(port): Enable parent window
     49 }
     50 
     51 bool FirstRunBubble::CloseOnEscape() {
     52   return true;
     53 }
     54 
     55 void FirstRunBubble::Observe(NotificationType type,
     56                              const NotificationSource& source,
     57                              const NotificationDetails& details) {
     58   DCHECK(type == NotificationType::BROWSER_THEME_CHANGED);
     59 
     60   if (theme_service_->UseGtkTheme()) {
     61     for (std::vector<GtkWidget*>::iterator it = labels_.begin();
     62          it != labels_.end(); ++it) {
     63       gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, NULL);
     64     }
     65   } else {
     66     for (std::vector<GtkWidget*>::iterator it = labels_.begin();
     67          it != labels_.end(); ++it) {
     68       gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, &gtk_util::kGdkBlack);
     69     }
     70   }
     71 }
     72 
     73 FirstRunBubble::FirstRunBubble(Profile* profile,
     74                                GtkWidget* anchor,
     75                                const gfx::Rect& rect,
     76                                FirstRun::BubbleType bubble_type)
     77     : profile_(profile),
     78       theme_service_(GtkThemeService::GetFrom(profile_)),
     79       anchor_(anchor),
     80       content_(NULL),
     81       bubble_(NULL) {
     82   content_ = gtk_vbox_new(FALSE, kInterLineSpacing);
     83   gtk_container_set_border_width(GTK_CONTAINER(content_), kContentBorder);
     84   g_signal_connect(content_, "destroy",
     85                    G_CALLBACK(&HandleDestroyThunk), this);
     86 
     87   int width_resource = 0;
     88   if (bubble_type == FirstRun::LARGE_BUBBLE) {
     89     width_resource = IDS_FIRSTRUNBUBBLE_DIALOG_WIDTH_CHARS;
     90     InitializeContentForLarge();
     91   } else if (bubble_type == FirstRun::OEM_BUBBLE) {
     92     width_resource = IDS_FIRSTRUNOEMBUBBLE_DIALOG_WIDTH_CHARS;
     93     InitializeContentForOEM();
     94   } else if (bubble_type == FirstRun::MINIMAL_BUBBLE) {
     95     width_resource = IDS_FIRSTRUN_MINIMAL_BUBBLE_DIALOG_WIDTH_CHARS;
     96     InitializeContentForMinimal();
     97   } else {
     98     NOTREACHED();
     99   }
    100 
    101   InitializeLabels(width_resource);
    102 
    103   InfoBubbleGtk::ArrowLocationGtk arrow_location =
    104       !base::i18n::IsRTL() ?
    105       InfoBubbleGtk::ARROW_LOCATION_TOP_LEFT :
    106       InfoBubbleGtk::ARROW_LOCATION_TOP_RIGHT;
    107   bubble_ = InfoBubbleGtk::Show(anchor_,
    108                                 &rect,
    109                                 content_,
    110                                 arrow_location,
    111                                 true,  // match_system_theme
    112                                 true,  // grab_input
    113                                 theme_service_,
    114                                 this);  // delegate
    115   if (!bubble_) {
    116     NOTREACHED();
    117     return;
    118   }
    119 
    120   registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
    121                  NotificationService::AllSources());
    122   theme_service_->InitThemesFor(this);
    123 }
    124 
    125 FirstRunBubble::~FirstRunBubble() {
    126 }
    127 
    128 void FirstRunBubble::InitializeContentForLarge() {
    129   GtkWidget* label1 = gtk_label_new(NULL);
    130   labels_.push_back(label1);
    131   char* markup = g_markup_printf_escaped(kSearchLabelMarkup,
    132       l10n_util::GetStringUTF8(IDS_FR_BUBBLE_TITLE).c_str());
    133   gtk_label_set_markup(GTK_LABEL(label1), markup);
    134   g_free(markup);
    135 
    136   GtkWidget* label2 = gtk_label_new(
    137       l10n_util::GetStringUTF8(IDS_FR_BUBBLE_SUBTEXT).c_str());
    138   labels_.push_back(label2);
    139 
    140   string16 search_engine = GetDefaultSearchEngineName(profile_);
    141   GtkWidget* label3 = gtk_label_new(
    142       l10n_util::GetStringFUTF8(IDS_FR_BUBBLE_QUESTION, search_engine).c_str());
    143   labels_.push_back(label3);
    144 
    145   GtkWidget* keep_button = gtk_button_new_with_label(
    146       l10n_util::GetStringFUTF8(IDS_FR_BUBBLE_OK, search_engine).c_str());
    147   GtkWidget* change_button = gtk_button_new_with_label(
    148       l10n_util::GetStringUTF8(IDS_FR_BUBBLE_CHANGE).c_str());
    149 
    150   gtk_box_pack_start(GTK_BOX(content_), label1, FALSE, FALSE, 0);
    151   gtk_box_pack_start(GTK_BOX(content_), label2, FALSE, FALSE, 0);
    152   // Leave an empty line.
    153   gtk_box_pack_start(GTK_BOX(content_), gtk_label_new(NULL), FALSE, FALSE, 0);
    154   gtk_box_pack_start(GTK_BOX(content_), label3, FALSE, FALSE, 0);
    155 
    156   GtkWidget* bottom = gtk_hbox_new(FALSE, 0);
    157   // We want the buttons on the right, so just use an expanding label to fill
    158   // all of the extra space on the left.
    159   gtk_box_pack_start(GTK_BOX(bottom), gtk_label_new(NULL), TRUE, TRUE, 0);
    160   gtk_box_pack_start(GTK_BOX(bottom), keep_button, FALSE, FALSE,
    161                      kButtonPadding);
    162   gtk_box_pack_start(GTK_BOX(bottom), change_button, FALSE, FALSE, 0);
    163 
    164   gtk_box_pack_start(GTK_BOX(content_), bottom, FALSE, FALSE, 0);
    165   // We want the focus to start on the keep entry, not on the change button.
    166   gtk_widget_grab_focus(keep_button);
    167 
    168   g_signal_connect(keep_button, "clicked",
    169                    G_CALLBACK(&HandleKeepButtonThunk), this);
    170   g_signal_connect(change_button, "clicked",
    171                    G_CALLBACK(&HandleChangeButtonThunk), this);
    172 }
    173 
    174 void FirstRunBubble::InitializeContentForOEM() {
    175   NOTIMPLEMENTED() << "Falling back to minimal bubble";
    176   InitializeContentForMinimal();
    177 }
    178 
    179 void FirstRunBubble::InitializeContentForMinimal() {
    180   GtkWidget* label1 = gtk_label_new(NULL);
    181   labels_.push_back(label1);
    182   char* markup = g_markup_printf_escaped(kSearchLabelMarkup,
    183       l10n_util::GetStringFUTF8(
    184           IDS_FR_SE_BUBBLE_TITLE,
    185           GetDefaultSearchEngineName(profile_)).c_str());
    186   gtk_label_set_markup(GTK_LABEL(label1), markup);
    187   g_free(markup);
    188 
    189   GtkWidget* label2 =
    190       gtk_label_new(l10n_util::GetStringUTF8(IDS_FR_BUBBLE_SUBTEXT).c_str());
    191   labels_.push_back(label2);
    192 
    193   gtk_box_pack_start(GTK_BOX(content_), label1, FALSE, FALSE, 0);
    194   gtk_box_pack_start(GTK_BOX(content_), label2, FALSE, FALSE, 0);
    195 }
    196 
    197 void FirstRunBubble::InitializeLabels(int width_resource) {
    198   int width = -1;
    199 
    200   gtk_util::GetWidgetSizeFromResources(
    201       anchor_, width_resource, 0, &width, NULL);
    202 
    203   for (size_t i = 0; i < labels_.size(); ++i) {
    204     // Resize the labels so that they don't wrap more than necessary.  We leave
    205     // |content_| unsized so that it'll expand as needed to hold the other
    206     // widgets -- the buttons may be wider than |width| on high-DPI displays.
    207     gtk_util::SetLabelWidth(labels_[i], width);
    208   }
    209 }
    210 
    211 void FirstRunBubble::HandleDestroy(GtkWidget* sender) {
    212   content_ = NULL;
    213   delete this;
    214 }
    215 
    216 void FirstRunBubble::HandleKeepButton(GtkWidget* sender) {
    217   bubble_->Close();
    218 }
    219 
    220 void FirstRunBubble::HandleChangeButton(GtkWidget* sender) {
    221   bubble_->Close();
    222   Browser* browser = BrowserList::GetLastActive();
    223   DCHECK(browser);
    224   browser->OpenSearchEngineOptionsDialog();
    225 }
    226