summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views/first_run_search_engine_view.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/views/first_run_search_engine_view.cc')
-rw-r--r--chrome/browser/views/first_run_search_engine_view.cc450
1 files changed, 450 insertions, 0 deletions
diff --git a/chrome/browser/views/first_run_search_engine_view.cc b/chrome/browser/views/first_run_search_engine_view.cc
new file mode 100644
index 0000000..48eed28
--- /dev/null
+++ b/chrome/browser/views/first_run_search_engine_view.cc
@@ -0,0 +1,450 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/views/first_run_search_engine_view.h"
+
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/i18n/rtl.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/search_engines/template_url_model.h"
+#include "gfx/font.h"
+#include "grit/browser_resources.h"
+#include "grit/chromium_strings.h"
+#include "grit/google_chrome_strings.h"
+#include "grit/generated_resources.h"
+#include "grit/locale_settings.h"
+#include "grit/theme_resources.h"
+#include "views/controls/button/button.h"
+#include "views/controls/image_view.h"
+#include "views/controls/label.h"
+#include "views/controls/separator.h"
+#include "views/standard_layout.h"
+#include "views/view_text_utils.h"
+#include "views/window/window.h"
+
+namespace {
+
+// These strings mark the embedded link in IDS_FIRSTRUN_SEARCH_SUBTEXT.
+const wchar_t* kBeginLink = L"BEGIN_LINK_SE";
+const wchar_t* kEndLink = L"END_LINK_SE";
+
+// Represents an id for which we have no logo.
+const int kNoLogo = -1;
+
+// Size to scale logos down to if showing 4 instead of 3 choices. Logo images
+// are all originally sized at 180 x 120 pixels, with the logo text baseline
+// located 74 pixels beneath the top of the image.
+const int kSmallLogoWidth = 120;
+const int kSmallLogoHeight = 80;
+
+// Used to pad text label height so it fits nicely in view.
+const int kLabelPadding = 25;
+
+// Maps the prepopulated id of a search engine to its logo.
+struct SearchEngineLogo {
+ // The unique id for a prepopulated search engine; see PrepopulatedEngine
+ // struct in template_url_prepopulate_data.cc.
+ int search_engine_id;
+
+ // The IDR that represents the logo image for a given search engine.
+ int logo_idr;
+};
+
+// Mapping of prepopulate_id to logo for each search engine.
+SearchEngineLogo kSearchEngineLogos[] = {
+ { 1, IDR_SEARCH_ENGINE_LOGO_GOOGLE },
+ { 2, IDR_SEARCH_ENGINE_LOGO_YAHOO },
+ { 3, IDR_SEARCH_ENGINE_LOGO_BING },
+};
+
+int GetSearchEngineLogo(const TemplateURL* template_url) {
+ TemplateURL::IDType id = template_url->prepopulate_id();
+ if (id > 0) {
+ for (size_t i = 0; i < arraysize(kSearchEngineLogos); ++i) {
+ if (kSearchEngineLogos[i].search_engine_id == id) {
+ return kSearchEngineLogos[i].logo_idr;
+ }
+ }
+ }
+ // |id| is either not found, or does not exist.
+ return kNoLogo;
+}
+
+} // namespace
+
+SearchEngineChoice::SearchEngineChoice(views::ButtonListener* listener,
+ const TemplateURL* search_engine,
+ bool use_small_logos)
+ : NativeButton(listener, l10n_util::GetString(IDS_FR_SEARCH_CHOOSE)),
+ is_image_label_(false),
+ search_engine_(search_engine) {
+ bool use_images = false;
+#if defined(GOOGLE_CHROME_BUILD)
+ use_images = true;
+#endif
+ int logo_id = GetSearchEngineLogo(search_engine_);
+ if (use_images && logo_id != kNoLogo) {
+ is_image_label_ = true;
+ views::ImageView* logo_image = new views::ImageView();
+ SkBitmap* logo_bmp =
+ ResourceBundle::GetSharedInstance().GetBitmapNamed(logo_id);
+ logo_image->SetImage(logo_bmp);
+ if (use_small_logos)
+ logo_image->SetImageSize(gfx::Size(kSmallLogoWidth, kSmallLogoHeight));
+ // Tooltip text provides accessibility.
+ logo_image->SetTooltipText(search_engine_->short_name());
+ choice_view_.reset(logo_image);
+ } else {
+ // No logo -- we must show a text label.
+ views::Label* logo_label = new views::Label(search_engine_->short_name());
+ logo_label->SetColor(SK_ColorDKGRAY);
+ logo_label->SetFont(logo_label->font().DeriveFont(2, gfx::Font::NORMAL));
+ logo_label->SetHorizontalAlignment(views::Label::ALIGN_CENTER);
+ logo_label->SetTooltipText(search_engine_->short_name());
+ choice_view_.reset(logo_label);
+ }
+}
+
+int SearchEngineChoice::GetChoiceViewWidth() {
+ return choice_view_->GetPreferredSize().width();
+}
+
+int SearchEngineChoice::GetChoiceViewHeight() {
+ if (!is_image_label_) {
+ // Labels need to be padded to look nicer.
+ return choice_view_->GetPreferredSize().height() + kLabelPadding;
+ } else {
+ return choice_view_->GetPreferredSize().height();
+ }
+}
+
+void SearchEngineChoice::SetChoiceViewBounds(int x, int y, int width,
+ int height) {
+ choice_view_->SetBounds(x, y, width, height);
+}
+
+FirstRunSearchEngineView::FirstRunSearchEngineView(
+ SearchEngineViewObserver* observer, Profile* profile)
+ : profile_(profile),
+ observer_(observer),
+ text_direction_is_rtl_(base::i18n::IsRTL()) {
+ DCHECK(observer);
+ // Don't show ourselves until all the search engines have loaded from
+ // the profile -- otherwise we have nothing to show.
+ SetVisible(false);
+
+ // Start loading the search engines for the given profile.
+ search_engines_model_ = profile_->GetTemplateURLModel();
+ if (search_engines_model_) {
+ DCHECK(!search_engines_model_->loaded());
+ search_engines_model_->AddObserver(this);
+ search_engines_model_->Load();
+ } else {
+ NOTREACHED();
+ }
+ SetupControls();
+}
+
+FirstRunSearchEngineView::~FirstRunSearchEngineView() {
+ search_engines_model_->RemoveObserver(this);
+}
+
+void FirstRunSearchEngineView::ButtonPressed(views::Button* sender,
+ const views::Event& event) {
+ observer_->SearchEngineChosen(
+ static_cast<SearchEngineChoice*>(sender)->GetSearchEngine());
+}
+
+void FirstRunSearchEngineView::OnTemplateURLModelChanged() {
+ using views::ImageView;
+
+ // We only watch the search engine model change once, on load. Remove
+ // observer so we don't try to redraw if engines change under us.
+ search_engines_model_->RemoveObserver(this);
+
+ // Add search engines in search_engines_model_ to buttons list. The
+ // first three will always be from prepopulated data.
+ std::vector<const TemplateURL*> template_urls =
+ search_engines_model_->GetTemplateURLs();
+ std::vector<const TemplateURL*>::iterator search_engine_iter;
+
+ // Is user's default search engine included in first three prepopulated
+ // set? If not, we need to expand the dialog to include a fourth engine.
+ const TemplateURL* default_search_engine =
+ search_engines_model_->GetDefaultSearchProvider();
+ // If the user's default choice is not in the first three search engines
+ // in template_urls, store it in |default_choice| and provide it as a
+ // fourth option.
+ SearchEngineChoice* default_choice = NULL;
+
+ // First, see if we have 4 logos to show (in which case we use small logos).
+ // We show 4 logos when the default search engine the user has chosen is
+ // not one of the first three prepopulated engines.
+ if (template_urls.size() > 3) {
+ for (search_engine_iter = template_urls.begin() + 3;
+ search_engine_iter != template_urls.end();
+ ++search_engine_iter) {
+ if (default_search_engine == *search_engine_iter) {
+ default_choice = new SearchEngineChoice(this, *search_engine_iter,
+ true);
+ }
+ }
+ }
+
+ // Now that we know what size the logos should be, create new search engine
+ // choices for the view:
+ for (search_engine_iter = template_urls.begin();
+ search_engine_iter < template_urls.begin() + 3;
+ ++search_engine_iter) {
+ // Push first three engines into buttons:
+ SearchEngineChoice* choice = new SearchEngineChoice(this,
+ *search_engine_iter, default_choice != NULL);
+ search_engine_choices_.push_back(choice);
+ AddChildView(choice->GetView());
+ AddChildView(choice);
+ }
+ // Push the default choice to the fourth position.
+ if (default_choice) {
+ search_engine_choices_.push_back(default_choice);
+ AddChildView(default_choice);
+ AddChildView(default_choice->GetView());
+ }
+
+ // Now that we know how many logos to show, lay out and become visible.
+ SetVisible(true);
+ Layout();
+ SchedulePaint();
+}
+
+void FirstRunSearchEngineView::OnKeywordEditorClosing(bool default_set) {
+ // If the search engine has been set in the KeywordEditor, pass NULL
+ // to the observer to show that we did not choose a search engine in this
+ // dialog, and to notify that we're done with the selection process.
+ if (default_set)
+ observer_->SearchEngineChosen(NULL);
+ // Else, the user cancelled the KeywordEditor, so continue with this dialog.
+}
+
+gfx::Size FirstRunSearchEngineView::GetPreferredSize() {
+ return views::Window::GetLocalizedContentsSize(
+ IDS_FIRSTRUN_SEARCH_ENGINE_SELECTION_WIDTH_CHARS,
+ IDS_FIRSTRUN_SEARCH_ENGINE_SELECTION_HEIGHT_LINES);
+}
+
+void FirstRunSearchEngineView::LinkActivated(views::Link* source,
+ int event_flags) {
+ // The KeywordEditor is going to modify search_engines_model_, so
+ // relinquish our observership so we don't try to redraw.
+ search_engines_model_->RemoveObserver(this);
+ // Launch search engine editing window from browser options dialog.
+ KeywordEditorView::ShowAndObserve(profile_, this);
+}
+
+void FirstRunSearchEngineView::SetupControls() {
+ using views::Background;
+ using views::ImageView;
+ using views::Label;
+ using views::Link;
+ using views::NativeButton;
+
+ int label_width = GetPreferredSize().width() - 2 * kPanelHorizMargin;
+
+ set_background(Background::CreateSolidBackground(SK_ColorWHITE));
+
+ // Add title and text asking the user to choose a search engine:
+ title_label_ = new Label(l10n_util::GetString(
+ IDS_FR_SEARCH_MAIN_LABEL));
+ title_label_->SetColor(SK_ColorBLACK);
+ title_label_->SetFont(title_label_->font().DeriveFont(1, gfx::Font::BOLD));
+ title_label_->SetMultiLine(true);
+ title_label_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+ title_label_->SizeToFit(label_width);
+ AddChildView(title_label_);
+
+ text_label_ = new Label(l10n_util::GetStringF(IDS_FR_SEARCH_TEXT,
+ l10n_util::GetString(IDS_PRODUCT_NAME)));
+ text_label_->SetColor(SK_ColorBLACK);
+ text_label_->SetMultiLine(true);
+ text_label_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+ text_label_->SizeToFit(label_width);
+ AddChildView(text_label_);
+
+ // The first separator marks the start of the search engine choice panel.
+ separator_1_ = new views::Separator;
+ AddChildView(separator_1_);
+
+ // The second separator marks the end of the search engine choice panel.
+ separator_2_ = new views::Separator;
+ AddChildView(separator_2_);
+
+ // Parse out the link to the internal search engine options dialog.
+ std::wstring subtext = l10n_util::GetStringF(IDS_FR_SEARCH_SUBTEXT,
+ l10n_util::GetString(IDS_PRODUCT_NAME));
+
+ size_t link_begin = subtext.find(kBeginLink);
+ DCHECK(link_begin != std::wstring::npos);
+ size_t link_end = subtext.find(kEndLink);
+ DCHECK(link_end != std::wstring::npos);
+
+ subtext_label_1_ = new Label(subtext.substr(0, link_begin));
+ options_link_ = new views::Link(subtext.substr(link_begin +
+ wcslen(kBeginLink), link_end - link_begin - wcslen(kBeginLink)));
+ AddChildView(options_link_);
+ options_link_->SetController(this);
+ subtext_label_2_ = new Label(subtext.substr(link_end + wcslen(kEndLink)));
+
+ // This label is never actually shown -- it's just used to place the subtext
+ // strings correctly in the view.
+ std::wstring dummy_label = subtext.substr(wcslen(kBeginLink) +
+ wcslen(kEndLink) + 4);
+ dummy_subtext_label_ = new Label(dummy_label);
+ dummy_subtext_label_->SetMultiLine(true);
+ dummy_subtext_label_->SizeToFit(label_width);
+}
+
+void FirstRunSearchEngineView::Layout() {
+ // General vertical spacing between elements:
+ const int kVertSpacing = 8;
+ // Vertical spacing between the logo + button section and the separators:
+ const int kUpperLogoMargin = 40;
+ const int kLowerLogoMargin = 65;
+ // Percentage of vertical space around logos to use for upper padding.
+ const double kUpperPaddingPercent = 0.1;
+
+ int num_choices = search_engine_choices_.size();
+
+ // Title and text above top separator.
+ int label_width = GetPreferredSize().width() - 2 * kPanelHorizMargin;
+ int label_height = GetPreferredSize().height() - 2 * kPanelVertMargin;
+
+ title_label_->SetBounds(kPanelHorizMargin, kPanelVertMargin,
+ label_width, title_label_->GetPreferredSize().height());
+
+ int next_v_space = title_label_->y() +
+ title_label_->height() + kVertSpacing;
+
+ text_label_->SetBounds(kPanelHorizMargin, next_v_space,
+ label_width,
+ text_label_->GetPreferredSize().height());
+ next_v_space = text_label_->y() +
+ text_label_->height() + kVertSpacing;
+
+ separator_1_->SetBounds(kPanelHorizMargin, next_v_space, label_width,
+ separator_1_->GetPreferredSize().height());
+
+ // Set the logos and buttons between the upper and lower separators:
+ if (num_choices > 0) {
+ int logo_width = search_engine_choices_[0]->GetChoiceViewWidth();
+ int logo_height = search_engine_choices_[0]->GetChoiceViewHeight();
+ int button_width = search_engine_choices_[0]->GetPreferredSize().width();
+ int button_height = search_engine_choices_[0]->GetPreferredSize().height();
+ int lower_section_height = dummy_subtext_label_->height() + kVertSpacing +
+ separator_2_->GetPreferredSize().height();
+ int logo_section_height = logo_height + kVertSpacing + button_height;
+ int lower_section_start = label_height + kPanelVertMargin -
+ lower_section_height;
+ int upper_logo_margin =
+ static_cast<int>((lower_section_start - separator_1_->y() -
+ separator_1_->height() - logo_section_height) *
+ kUpperPaddingPercent);
+
+ next_v_space = separator_1_->y() + separator_1_->height() +
+ upper_logo_margin;
+
+ // The search engine logos (which all have equal size):
+ int logo_padding =
+ (label_width - (num_choices * logo_width)) / (num_choices + 1);
+
+ search_engine_choices_[0]->SetChoiceViewBounds(
+ kPanelHorizMargin + logo_padding, next_v_space, logo_width,
+ logo_height);
+
+ int next_h_space = search_engine_choices_[0]->GetView()->x() +
+ logo_width + logo_padding;
+ search_engine_choices_[1]->SetChoiceViewBounds(
+ next_h_space, next_v_space, logo_width, logo_height);
+
+ next_h_space = search_engine_choices_[1]->GetView()->x() + logo_width +
+ logo_padding;
+ search_engine_choices_[2]->SetChoiceViewBounds(
+ next_h_space, next_v_space, logo_width, logo_height);
+
+ if (num_choices > 3) {
+ next_h_space = search_engine_choices_[2]->GetView()->x() + logo_width +
+ logo_padding;
+ search_engine_choices_[3]->SetChoiceViewBounds(
+ next_h_space, next_v_space, logo_width, logo_height);
+ }
+
+ next_v_space = search_engine_choices_[0]->GetView()->y() + logo_height +
+ kVertSpacing;
+
+ // The buttons for search engine selection:
+ int button_padding = logo_padding + logo_width / 2 - button_width / 2;
+
+ search_engine_choices_[0]->SetBounds(kPanelHorizMargin + button_padding,
+ next_v_space, button_width,
+ button_height);
+
+ next_h_space = search_engine_choices_[0]->x() + logo_width + logo_padding;
+ search_engine_choices_[1]->SetBounds(next_h_space, next_v_space,
+ button_width, button_height);
+ next_h_space = search_engine_choices_[1]->x() + logo_width + logo_padding;
+ search_engine_choices_[2]->SetBounds(next_h_space, next_v_space,
+ button_width, button_height);
+
+ if (num_choices > 3) {
+ next_h_space = search_engine_choices_[2]->x() + logo_width +
+ logo_padding;
+ search_engine_choices_[3]->SetBounds(next_h_space, next_v_space,
+ button_width, button_height);
+ }
+
+ // Lower separator and text beneath it.
+ next_v_space = lower_section_start;
+ } // if (search_engine_choices.size() > 0)
+
+ separator_2_->SetBounds(kPanelHorizMargin, next_v_space, label_width,
+ separator_2_->GetPreferredSize().height());
+
+ next_v_space = separator_2_->y() + separator_2_->height() + kVertSpacing;
+
+ // This label is used by view_text_utils::DrawTextAndPositionUrl in order
+ // to figure out mirrored x positions for RTL languages. It only needs to
+ // provide the correct origin and width; height is not used.
+ dummy_subtext_label_->SetBounds(kPanelHorizMargin, next_v_space,
+ label_width, dummy_subtext_label_->height());
+}
+
+void FirstRunSearchEngineView::Paint(gfx::Canvas* canvas) {
+ views::View::Paint(canvas);
+
+ gfx::Rect link_rect;
+ gfx::Size position;
+ gfx::Font font =
+ ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
+ const gfx::Rect label_bounds = dummy_subtext_label_->bounds();
+
+ view_text_utils::DrawTextAndPositionUrl(canvas, dummy_subtext_label_,
+ subtext_label_1_->GetText(), options_link_, &link_rect, &position,
+ text_direction_is_rtl_, label_bounds, font);
+ view_text_utils::DrawTextAndPositionUrl(canvas, dummy_subtext_label_,
+ subtext_label_2_->GetText(), NULL, NULL, &position,
+ text_direction_is_rtl_, label_bounds, font);
+
+ options_link_->SetBounds(link_rect.x(), link_rect.y(), link_rect.width(),
+ link_rect.height());
+}
+
+std::wstring FirstRunSearchEngineView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_FIRSTRUN_DLG_TITLE);
+}
+
+void FirstRunSearchEngineView::WindowClosing() {
+ // User decided not to choose a search engine after all.
+ observer_->SearchEngineChosen(NULL);
+}
+