edit_search_engine_dialog.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2009 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/views/edit_search_engine_dialog.h"
6
7#include "app/l10n_util.h"
8#include "app/resource_bundle.h"
9#include "base/i18n/rtl.h"
10#include "base/string_util.h"
11#include "base/utf_string_conversions.h"
12#include "chrome/browser/search_engines/edit_search_engine_controller.h"
13#include "chrome/browser/search_engines/template_url.h"
14#include "googleurl/src/gurl.h"
15#include "grit/app_resources.h"
16#include "grit/generated_resources.h"
17#include "grit/theme_resources.h"
18#include "views/controls/label.h"
19#include "views/controls/image_view.h"
20#include "views/controls/table/table_view.h"
21#include "views/grid_layout.h"
22#include "views/standard_layout.h"
23#include "views/window/window.h"
24
25using views::GridLayout;
26using views::ImageView;
27using views::Textfield;
28
29
30namespace {
31// Converts a URL as understood by TemplateURL to one appropriate for display
32// to the user.
33std::wstring GetDisplayURL(const TemplateURL& turl) {
34  return turl.url() ? turl.url()->DisplayURL() : std::wstring();
35}
36}  // namespace
37
38namespace browser {
39
40void EditSearchEngine(gfx::NativeWindow parent,
41                      const TemplateURL* template_url,
42                      EditSearchEngineControllerDelegate* delegate,
43                      Profile* profile) {
44  EditSearchEngineDialog::Show(parent, template_url, delegate, profile);
45}
46
47}  // namespace browser
48
49EditSearchEngineDialog::EditSearchEngineDialog(
50    const TemplateURL* template_url,
51    EditSearchEngineControllerDelegate* delegate,
52    Profile* profile)
53    : controller_(new EditSearchEngineController(template_url,
54                                                 delegate,
55                                                 profile)) {
56  Init();
57}
58
59// static
60void EditSearchEngineDialog::Show(gfx::NativeWindow parent,
61                                  const TemplateURL* template_url,
62                                  EditSearchEngineControllerDelegate* delegate,
63                                  Profile* profile) {
64  EditSearchEngineDialog* contents =
65      new EditSearchEngineDialog(template_url, delegate, profile);
66  // Window interprets an empty rectangle as needing to query the content for
67  // the size as well as centering relative to the parent.
68  views::Window::CreateChromeWindow(parent, gfx::Rect(), contents);
69  contents->window()->Show();
70  contents->GetDialogClientView()->UpdateDialogButtons();
71  contents->title_tf_->SelectAll();
72  contents->title_tf_->RequestFocus();
73}
74
75bool EditSearchEngineDialog::IsModal() const {
76  return true;
77}
78
79std::wstring EditSearchEngineDialog::GetWindowTitle() const {
80  return l10n_util::GetString(controller_->template_url() ?
81      IDS_SEARCH_ENGINES_EDITOR_EDIT_WINDOW_TITLE :
82      IDS_SEARCH_ENGINES_EDITOR_NEW_WINDOW_TITLE);
83}
84
85bool EditSearchEngineDialog::IsDialogButtonEnabled(
86    MessageBoxFlags::DialogButton button) const {
87  if (button == MessageBoxFlags::DIALOGBUTTON_OK) {
88    return (controller_->IsKeywordValid(WideToUTF16(keyword_tf_->text())) &&
89            controller_->IsTitleValid(WideToUTF16(title_tf_->text())) &&
90            controller_->IsURLValid(WideToUTF8(url_tf_->text())));
91  }
92  return true;
93}
94
95bool EditSearchEngineDialog::Cancel() {
96  controller_->CleanUpCancelledAdd();
97  return true;
98}
99
100bool EditSearchEngineDialog::Accept() {
101  controller_->AcceptAddOrEdit(WideToUTF16(title_tf_->text()),
102                               WideToUTF16(keyword_tf_->text()),
103                               WideToUTF8(url_tf_->text()));
104  return true;
105}
106
107views::View* EditSearchEngineDialog::GetContentsView() {
108  return this;
109}
110
111void EditSearchEngineDialog::ContentsChanged(Textfield* sender,
112                                             const std::wstring& new_contents) {
113  GetDialogClientView()->UpdateDialogButtons();
114  UpdateImageViews();
115}
116
117bool EditSearchEngineDialog::HandleKeystroke(
118    Textfield* sender,
119    const views::Textfield::Keystroke& key) {
120  return false;
121}
122
123void EditSearchEngineDialog::Init() {
124  // Create the views we'll need.
125  if (controller_->template_url()) {
126    title_tf_ =
127        CreateTextfield(controller_->template_url()->short_name(), false);
128    keyword_tf_ = CreateTextfield(controller_->template_url()->keyword(), true);
129    url_tf_ =
130        CreateTextfield(GetDisplayURL(*controller_->template_url()), false);
131    // We don't allow users to edit prepopulate URLs. This is done as
132    // occasionally we need to update the URL of prepopulated TemplateURLs.
133    url_tf_->SetReadOnly(controller_->template_url()->prepopulate_id() != 0);
134  } else {
135    title_tf_ = CreateTextfield(std::wstring(), false);
136    keyword_tf_ = CreateTextfield(std::wstring(), true);
137    url_tf_ = CreateTextfield(std::wstring(), false);
138  }
139  title_iv_ = new ImageView();
140  keyword_iv_ = new ImageView();
141  url_iv_ = new ImageView();
142
143  UpdateImageViews();
144
145  const int related_x = kRelatedControlHorizontalSpacing;
146  const int related_y = kRelatedControlVerticalSpacing;
147  const int unrelated_y = kUnrelatedControlVerticalSpacing;
148
149  // View and GridLayout take care of deleting GridLayout for us.
150  GridLayout* layout = CreatePanelGridLayout(this);
151  SetLayoutManager(layout);
152
153  // Define the structure of the layout.
154
155  // For the buttons.
156  views::ColumnSet* column_set = layout->AddColumnSet(0);
157  column_set->AddPaddingColumn(1, 0);
158  column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
159                        GridLayout::USE_PREF, 0, 0);
160  column_set->AddPaddingColumn(0, related_x);
161  column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
162                        GridLayout::USE_PREF, 0, 0);
163  column_set->LinkColumnSizes(1, 3, -1);
164
165  // For the Textfields.
166  column_set = layout->AddColumnSet(1);
167  column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
168                        GridLayout::USE_PREF, 0, 0);
169  column_set->AddPaddingColumn(0, related_x);
170  column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
171                        GridLayout::USE_PREF, 0, 0);
172  column_set->AddPaddingColumn(0, related_x);
173  column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
174                        GridLayout::USE_PREF, 0, 0);
175
176  // For the description.
177  column_set = layout->AddColumnSet(2);
178  column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
179                        GridLayout::USE_PREF, 0, 0);
180
181  // Add the contents.
182  layout->StartRow(0, 1);
183  layout->AddView(CreateLabel(IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_LABEL));
184  layout->AddView(title_tf_);
185  layout->AddView(title_iv_);
186
187  layout->StartRowWithPadding(0, 1, 0, related_y);
188  layout->AddView(CreateLabel(IDS_SEARCH_ENGINES_EDITOR_KEYWORD_LABEL));
189  layout->AddView(keyword_tf_);
190  layout->AddView(keyword_iv_);
191
192  layout->StartRowWithPadding(0, 1, 0, related_y);
193  layout->AddView(CreateLabel(IDS_SEARCH_ENGINES_EDITOR_URL_LABEL));
194  layout->AddView(url_tf_);
195  layout->AddView(url_iv_);
196
197  // On RTL UIs (such as Arabic and Hebrew) the description text is not
198  // displayed correctly since it contains the substring "%s". This substring
199  // is not interpreted by the Unicode BiDi algorithm as an LTR string and
200  // therefore the end result is that the following right to left text is
201  // displayed: ".three two s% one" (where 'one', 'two', etc. are words in
202  // Hebrew).
203  //
204  // In order to fix this problem we transform the substring "%s" so that it
205  // is displayed correctly when rendered in an RTL context.
206  layout->StartRowWithPadding(0, 2, 0, unrelated_y);
207  std::wstring description =
208      l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_URL_DESCRIPTION_LABEL);
209  if (base::i18n::IsRTL()) {
210    const std::wstring reversed_percent(L"s%");
211    std::wstring::size_type percent_index =
212        description.find(L"%s", static_cast<std::wstring::size_type>(0));
213    if (percent_index != std::wstring::npos)
214      description.replace(percent_index,
215                          reversed_percent.length(),
216                          reversed_percent);
217  }
218
219  views::Label* description_label = new views::Label(description);
220  description_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
221  layout->AddView(description_label);
222
223  layout->AddPaddingRow(0, related_y);
224}
225
226views::Label* EditSearchEngineDialog::CreateLabel(int message_id) {
227  views::Label* label = new views::Label(l10n_util::GetString(message_id));
228  label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
229  return label;
230}
231
232Textfield* EditSearchEngineDialog::CreateTextfield(const std::wstring& text,
233                                                   bool lowercase) {
234  Textfield* text_field = new Textfield(
235      lowercase ? Textfield::STYLE_LOWERCASE : Textfield::STYLE_DEFAULT);
236  text_field->SetText(text);
237  text_field->SetController(this);
238  return text_field;
239}
240
241void EditSearchEngineDialog::UpdateImageViews() {
242  UpdateImageView(keyword_iv_,
243                  controller_->IsKeywordValid(WideToUTF16(keyword_tf_->text())),
244                  IDS_SEARCH_ENGINES_INVALID_KEYWORD_TT);
245  UpdateImageView(url_iv_, controller_->IsURLValid(WideToUTF8(url_tf_->text())),
246                  IDS_SEARCH_ENGINES_INVALID_URL_TT);
247  UpdateImageView(title_iv_,
248                  controller_->IsTitleValid(WideToUTF16(title_tf_->text())),
249                  IDS_SEARCH_ENGINES_INVALID_TITLE_TT);
250}
251
252void EditSearchEngineDialog::UpdateImageView(ImageView* image_view,
253                                             bool is_valid,
254                                             int invalid_message_id) {
255  if (is_valid) {
256    image_view->SetTooltipText(std::wstring());
257    image_view->SetImage(
258        ResourceBundle::GetSharedInstance().GetBitmapNamed(
259            IDR_INPUT_GOOD));
260  } else {
261    image_view->SetTooltipText(l10n_util::GetString(invalid_message_id));
262    image_view->SetImage(
263        ResourceBundle::GetSharedInstance().GetBitmapNamed(
264            IDR_INPUT_ALERT));
265  }
266}
267