| 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 |