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/views/default_search_view.h"
6
7#include <string>
8
9#include "base/utf_string_conversions.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/search_engines/template_url.h"
12#include "chrome/browser/search_engines/template_url_model.h"
13#include "chrome/browser/search_engines/template_url_prepopulate_data.h"
14#include "content/browser/tab_contents/tab_contents.h"
15#include "grit/generated_resources.h"
16#include "grit/locale_settings.h"
17#include "grit/theme_resources.h"
18#include "ui/base/message_box_flags.h"
19#include "ui/base/l10n/l10n_util.h"
20#include "ui/base/resource/resource_bundle.h"
21#include "ui/gfx/canvas.h"
22#include "views/controls/button/native_button.h"
23#include "views/controls/image_view.h"
24#include "views/controls/label.h"
25#include "views/layout/grid_layout.h"
26#include "views/layout/layout_constants.h"
27#include "views/window/dialog_client_view.h"
28#include "views/window/window.h"
29
30namespace {
31
32// Returns a short name and logo resource id for the given host.
33void GetShortNameAndLogoId(PrefService* prefs,
34                           const TemplateURL* turl,
35                           std::wstring* short_name,
36                           int* logo_id) {
37  DCHECK(prefs);
38  DCHECK(turl);
39  DCHECK(short_name);
40  DCHECK(logo_id);
41
42  GURL url = TemplateURLModel::GenerateSearchURL(turl);
43  scoped_ptr<TemplateURL> built_in_data(
44      TemplateURLPrepopulateData::GetEngineForOrigin(prefs, url));
45
46  // Use the built-in information to generate the short name (to ensure
47  // that we don't use a name given by the search engine to itself which
48  // in the worst case could be misleading).
49  if (built_in_data.get()) {
50    *short_name = built_in_data->short_name();
51    *logo_id = built_in_data->logo_id();
52  } else {
53    *short_name = UTF8ToWide(url.host()).c_str();
54    *logo_id = kNoSearchEngineLogo;
55  }
56}
57
58views::Label* CreateProviderLabel(int message_id) {
59  views::Label* choice_label =
60      new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(message_id)));
61  choice_label->SetColor(SK_ColorBLACK);
62  choice_label->SetFont(
63      choice_label->font().DeriveFont(1, gfx::Font::NORMAL));
64  return choice_label;
65}
66
67views::View* CreateProviderLogo(
68    int logo_id,
69    const std::wstring& short_name) {
70  views::View* logo_view = NULL;
71
72  // The width for the "logo" element when text is displayed.
73  const int kTextLogoWidth = 132;
74
75  bool use_images = false;
76#if defined(GOOGLE_CHROME_BUILD)
77  use_images = true;
78#endif
79
80  if (use_images && logo_id != kNoSearchEngineLogo) {
81    views::ImageView* logo_image = new views::ImageView();
82    SkBitmap* logo_bmp =
83        ResourceBundle::GetSharedInstance().GetBitmapNamed(logo_id);
84    logo_image->SetImage(logo_bmp);
85    logo_image->SetTooltipText(short_name);
86    logo_view = logo_image;
87  } else {
88    // No logo -- show a text label.
89    views::Label* logo_label = new views::Label(short_name);
90    logo_label->SetColor(SK_ColorDKGRAY);
91    logo_label->SetFont(logo_label->font().DeriveFont(3, gfx::Font::BOLD));
92    logo_label->SetHorizontalAlignment(views::Label::ALIGN_CENTER);
93    // Tooltip text provides accessibility for low-vision users.
94    logo_label->SetTooltipText(short_name);
95    logo_view = logo_label;
96  }
97
98  return logo_view;
99}
100views::NativeButton* CreateProviderChoiceButton(
101    views::ButtonListener* listener,
102    int message_id,
103    const std::wstring& short_name) {
104  return new views::NativeButton(listener, UTF16ToWide(
105      l10n_util::GetStringFUTF16(message_id, WideToUTF16(short_name))));
106}
107
108}  // namespace
109
110// static
111void DefaultSearchView::Show(TabContents* tab_contents,
112                             TemplateURL* default_url,
113                             TemplateURLModel* template_url_model) {
114  scoped_ptr<TemplateURL> template_url(default_url);
115  if (!template_url_model->CanMakeDefault(default_url) ||
116      default_url->url()->GetHost().empty())
117    return;
118
119  // When the window closes, it will delete itself.
120  new DefaultSearchView(tab_contents, template_url.release(),
121                        template_url_model);
122}
123
124DefaultSearchView::~DefaultSearchView() {
125}
126
127void DefaultSearchView::OnPaint(gfx::Canvas* canvas) {
128  // Fill in behind the background image with the standard gray toolbar color.
129  canvas->FillRectInt(SkColorSetRGB(237, 238, 237), 0, 0, width(),
130                      background_image_->height());
131  // The rest of the dialog background should be white.
132  DCHECK(height() > background_image_->height());
133  canvas->FillRectInt(SK_ColorWHITE, 0, background_image_->height(), width(),
134                      height() - background_image_->height());
135}
136
137void DefaultSearchView::ButtonPressed(views::Button* sender,
138                                      const views::Event& event) {
139  views::DialogClientView* client = GetDialogClientView();
140  if (sender == proposed_provider_button_)
141    client->AcceptWindow();
142  else
143    client->CancelWindow();
144}
145
146std::wstring DefaultSearchView::GetWindowTitle() const {
147  return UTF16ToWide(l10n_util::GetStringUTF16(IDS_DEFAULT_SEARCH_TITLE));
148}
149
150views::View* DefaultSearchView::GetInitiallyFocusedView() {
151  return default_provider_button_;
152}
153
154views::View* DefaultSearchView::GetContentsView() {
155  return this;
156}
157
158int DefaultSearchView::GetDialogButtons() const {
159  return ui::MessageBoxFlags::DIALOGBUTTON_NONE;
160}
161
162bool DefaultSearchView::Accept() {
163  // Check this again in case the default became managed while this dialog was
164  // displayed.
165  TemplateURL* set_as_default = proposed_turl_.get();
166  if (!template_url_model_->CanMakeDefault(set_as_default))
167    return true;
168
169  template_url_model_->Add(proposed_turl_.release());
170  template_url_model_->SetDefaultSearchProvider(set_as_default);
171  return true;
172}
173
174DefaultSearchView::DefaultSearchView(TabContents* tab_contents,
175                                     TemplateURL* proposed_default_turl,
176                                     TemplateURLModel* template_url_model)
177    : background_image_(NULL),
178      default_provider_button_(NULL),
179      proposed_provider_button_(NULL),
180      proposed_turl_(proposed_default_turl),
181      template_url_model_(template_url_model) {
182  PrefService* prefs = tab_contents->profile()->GetPrefs();
183  SetupControls(prefs);
184
185  // Show the dialog.
186  tab_contents->CreateConstrainedDialog(this);
187}
188
189void DefaultSearchView::SetupControls(PrefService* prefs) {
190  using views::ColumnSet;
191  using views::GridLayout;
192  using views::ImageView;
193  using views::Label;
194
195  // Column set id's.
196  const int kWholeDialogViewSetId = 0;
197  const int kPaddedWholeDialogViewSetId = 1;
198  const int kChoicesViewSetId = 2;
199
200  // Set up the information for the proposed default.
201  std::wstring proposed_short_name;
202  int proposed_logo_id = kNoSearchEngineLogo;
203  GetShortNameAndLogoId(prefs,
204                        proposed_turl_.get(),
205                        &proposed_short_name,
206                        &proposed_logo_id);
207  if (proposed_logo_id != kNoSearchEngineLogo)
208    proposed_turl_->set_logo_id(proposed_logo_id);
209
210
211  // Set up the information for the current default.
212  std::wstring default_short_name;
213  int default_logo_id = kNoSearchEngineLogo;
214  GetShortNameAndLogoId(prefs,
215                        template_url_model_->GetDefaultSearchProvider(),
216                        &default_short_name,
217                        &default_logo_id);
218
219  // Now set-up the dialog contents.
220  GridLayout* layout = new views::GridLayout(this);
221  layout->SetInsets(0, 0, views::kPanelVertMargin, 0);
222  SetLayoutManager(layout);
223
224  // Add a column set that spans the whole dialog.
225  ColumnSet* whole_dialog_column_set =
226      layout->AddColumnSet(kWholeDialogViewSetId);
227  whole_dialog_column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
228                                     1, GridLayout::FIXED,
229                                     views::Window::GetLocalizedContentsWidth(
230                                         IDS_DEFAULT_SEARCH_WIDTH_CHARS),
231                                     0);
232
233  // Add a column set that spans the whole dialog but obeying padding.
234  ColumnSet* padded_whole_dialog_column_set =
235      layout->AddColumnSet(kPaddedWholeDialogViewSetId);
236  padded_whole_dialog_column_set->AddPaddingColumn(1, views::kPanelVertMargin);
237  padded_whole_dialog_column_set->AddColumn(
238      GridLayout::LEADING, GridLayout::LEADING,
239      1, GridLayout::USE_PREF, 0, 0);
240  padded_whole_dialog_column_set->AddPaddingColumn(1, views::kPanelVertMargin);
241
242  // Add a column set for the search engine choices.
243  ColumnSet* choices_column_set = layout->AddColumnSet(kChoicesViewSetId);
244  choices_column_set->AddPaddingColumn(1, views::kPanelVertMargin);
245  choices_column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER,
246                                1, GridLayout::USE_PREF, 0, 0);
247  choices_column_set->AddPaddingColumn(
248      1, views::kRelatedControlHorizontalSpacing);
249  choices_column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER,
250                                1, GridLayout::USE_PREF, 0, 0);
251  choices_column_set->LinkColumnSizes(0, 2, -1);
252  choices_column_set->AddPaddingColumn(1, views::kPanelVertMargin);
253
254  // Add the "search box" image.
255  layout->StartRow(0, kWholeDialogViewSetId);
256  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
257  background_image_ = new ImageView();
258  background_image_->SetImage(rb.GetBitmapNamed(IDR_SEARCH_ENGINE_DIALOG_TOP));
259  background_image_->EnableCanvasFlippingForRTLUI(true);
260  ImageView::Alignment horizontal_alignment =
261      base::i18n::IsRTL() ? ImageView::LEADING : ImageView::TRAILING;
262  background_image_->SetHorizontalAlignment(horizontal_alignment);
263  layout->AddView(background_image_);
264
265  // Add text informing the user about the requested default change.
266  layout->StartRowWithPadding(0, kPaddedWholeDialogViewSetId,
267                              1, views::kLabelToControlVerticalSpacing);
268  Label* summary_label = new Label(UTF16ToWide(l10n_util::GetStringFUTF16(
269      IDS_DEFAULT_SEARCH_SUMMARY,
270      WideToUTF16(proposed_short_name))));
271  summary_label->SetColor(SK_ColorBLACK);
272  summary_label->SetFont(
273      summary_label->font().DeriveFont(1, gfx::Font::NORMAL));
274  summary_label->SetHorizontalAlignment(Label::ALIGN_LEFT);
275  layout->AddView(summary_label);
276
277  // Add the labels for the tops of the choices.
278  layout->StartRowWithPadding(0, kChoicesViewSetId,
279                              0, views::kRelatedControlVerticalSpacing);
280  layout->AddView(CreateProviderLabel(IDS_DEFAULT_SEARCH_LABEL_CURRENT));
281  layout->AddView(CreateProviderLabel(IDS_DEFAULT_SEARCH_LABEL_PROPOSED));
282
283  // Add the logos.
284  layout->StartRowWithPadding(0, kChoicesViewSetId,
285                              0, views::kRelatedControlVerticalSpacing);
286  layout->AddView(CreateProviderLogo(default_logo_id, default_short_name));
287  layout->AddView(CreateProviderLogo(proposed_logo_id, proposed_short_name));
288
289  // Add the buttons.
290  layout->StartRowWithPadding(0, kChoicesViewSetId,
291                              0, views::kRelatedControlVerticalSpacing);
292  default_provider_button_ = CreateProviderChoiceButton(
293      this,
294      IDS_DEFAULT_SEARCH_PROMPT_CURRENT,
295      default_short_name);
296  layout->AddView(default_provider_button_);
297  proposed_provider_button_ = CreateProviderChoiceButton(
298      this,
299      IDS_DEFAULT_SEARCH_PROMPT_PROPOSED,
300      proposed_short_name);
301  layout->AddView(proposed_provider_button_);
302}
303