• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
Show(Profile * profile,GtkWidget * anchor,const gfx::Rect & rect,FirstRun::BubbleType bubble_type)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 
InfoBubbleClosing(InfoBubbleGtk * info_bubble,bool closed_by_escape)46 void FirstRunBubble::InfoBubbleClosing(InfoBubbleGtk* info_bubble,
47                                        bool closed_by_escape) {
48   // TODO(port): Enable parent window
49 }
50 
CloseOnEscape()51 bool FirstRunBubble::CloseOnEscape() {
52   return true;
53 }
54 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
FirstRunBubble(Profile * profile,GtkWidget * anchor,const gfx::Rect & rect,FirstRun::BubbleType bubble_type)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 
~FirstRunBubble()125 FirstRunBubble::~FirstRunBubble() {
126 }
127 
InitializeContentForLarge()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 
InitializeContentForOEM()174 void FirstRunBubble::InitializeContentForOEM() {
175   NOTIMPLEMENTED() << "Falling back to minimal bubble";
176   InitializeContentForMinimal();
177 }
178 
InitializeContentForMinimal()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 
InitializeLabels(int width_resource)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 
HandleDestroy(GtkWidget * sender)211 void FirstRunBubble::HandleDestroy(GtkWidget* sender) {
212   content_ = NULL;
213   delete this;
214 }
215 
HandleKeepButton(GtkWidget * sender)216 void FirstRunBubble::HandleKeepButton(GtkWidget* sender) {
217   bubble_->Close();
218 }
219 
HandleChangeButton(GtkWidget * sender)220 void FirstRunBubble::HandleChangeButton(GtkWidget* sender) {
221   bubble_->Close();
222   Browser* browser = BrowserList::GetLastActive();
223   DCHECK(browser);
224   browser->OpenSearchEngineOptionsDialog();
225 }
226