Logo Search packages:      
Sourcecode: chromium-browser version File versions  Download package

first_run_search_engine_view.cc

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

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/i18n/rtl.h"
#include "chrome/browser/options_window.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_model.h"
#include "chrome/browser/search_engines/template_url_prepopulate_data.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"

using TemplateURLPrepopulateData::SearchEngineType;

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;

int GetSearchEngineLogo(const TemplateURL* template_url) {
  typedef std::map<SearchEngineType, int> LogoMap;
  static LogoMap type_to_logo;
  if (type_to_logo.empty()) {
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_GOOGLE,
        IDR_SEARCH_ENGINE_LOGO_GOOGLE));
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOO,
        IDR_SEARCH_ENGINE_LOGO_YAHOO));
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_YAHOOJP,
        IDR_SEARCH_ENGINE_LOGO_YAHOOJP));
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_BING,
        IDR_SEARCH_ENGINE_LOGO_BING));
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_ASK,
        IDR_SEARCH_ENGINE_LOGO_ASK));
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_YANDEX,
        IDR_SEARCH_ENGINE_LOGO_YANDEX));
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_SEZNAM,
        IDR_SEARCH_ENGINE_LOGO_SEZNAM));
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_CENTRUM,
        IDR_SEARCH_ENGINE_LOGO_CENTRUMCZ));
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_NETSPRINT,
        IDR_SEARCH_ENGINE_LOGO_NETSPRINT));
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_VIRGILIO,
        IDR_SEARCH_ENGINE_LOGO_VIRGILIO));
    type_to_logo.insert(std::make_pair<SearchEngineType, int>(
        TemplateURLPrepopulateData::SEARCH_ENGINE_MAILRU,
        IDR_SEARCH_ENGINE_LOGO_MAILRU));
  }

  LogoMap::iterator logo = type_to_logo.find(
      TemplateURLPrepopulateData::GetSearchEngineType(template_url));
  if (logo != type_to_logo.end())
    return logo->second;

  // Logo 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_ = 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(3, gfx::Font::BOLD));
    logo_label->SetHorizontalAlignment(views::Label::ALIGN_CENTER);
    logo_label->SetTooltipText(search_engine_->short_name());
    logo_label->SetMultiLine(true);
    logo_label->SizeToFit(kSmallLogoWidth);
    choice_view_ = logo_label;
  }
}

int SearchEngineChoice::GetChoiceViewWidth() {
  if (is_image_label_)
    return choice_view_->GetPreferredSize().width();
  else
    return kSmallLogoWidth;
}

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(
    SearchEngineSelectionObserver* 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();

  // If we have fewer than three search engines, signal that the search engine
  // experiment is over, leaving imported default search engine setting intact.
  if (template_urls.size() < 3) {
    observer_->SearchEngineChosen(NULL);
    return;
  }

  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());  // The logo or text view.
    AddChildView(choice);  // The button associated with the choice.
  }
  // Push the default choice to the fourth position.
  if (default_choice) {
    search_engine_choices_.push_back(default_choice);
    AddChildView(default_choice->GetView());  // The logo or text view.
    AddChildView(default_choice);  // The button associated with the choice.
  }

  // Now that we know how many logos to show, lay out and become visible.
  SetVisible(true);
  Layout();
  SchedulePaint();
}

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. We pass
  // the observer to the KeywordEditor, who will tell the observer when a
  // search engine has been chosen.
  KeywordEditorView::ShowAndObserve(profile_, observer_);
  GetWindow()->Close();
}

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() {
  // Disable the close button.
  GetWindow()->EnableClose(false);

  // 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 kUpperPaddingPercentLarge = 0.1;
  const double kUpperPaddingPercentSmall = 0.3;

  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;
    double upper_padding_percent = (num_choices > 3) ?
        kUpperPaddingPercentSmall : kUpperPaddingPercentLarge;
    int upper_logo_margin =
        static_cast<int>((lower_section_start - separator_1_->y() -
                          separator_1_->height() - logo_section_height) *
                        upper_padding_percent);

    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);
}


Generated by  Doxygen 1.6.0   Back to index