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