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