Home | History | Annotate | Download | only in translate
      1 // Copyright 2013 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/translate/translate_bubble_view.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/i18n/string_compare.h"
     12 #include "base/memory/singleton.h"
     13 #include "base/metrics/histogram.h"
     14 #include "base/prefs/pref_service.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "chrome/browser/browser_process.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/translate/chrome_translate_client.h"
     19 #include "chrome/browser/translate/translate_service.h"
     20 #include "chrome/browser/ui/chrome_pages.h"
     21 #include "chrome/browser/ui/translate/translate_bubble_model_impl.h"
     22 #include "chrome/common/url_constants.h"
     23 #include "components/translate/core/browser/translate_download_manager.h"
     24 #include "components/translate/core/browser/translate_manager.h"
     25 #include "components/translate/core/browser/translate_ui_delegate.h"
     26 #include "content/public/browser/web_contents.h"
     27 #include "grit/generated_resources.h"
     28 #include "ui/base/l10n/l10n_util.h"
     29 #include "ui/base/models/combobox_model.h"
     30 #include "ui/base/models/simple_combobox_model.h"
     31 #include "ui/base/resource/resource_bundle.h"
     32 #include "ui/views/controls/button/checkbox.h"
     33 #include "ui/views/controls/button/label_button.h"
     34 #include "ui/views/controls/combobox/combobox.h"
     35 #include "ui/views/controls/label.h"
     36 #include "ui/views/controls/link.h"
     37 #include "ui/views/layout/box_layout.h"
     38 #include "ui/views/layout/grid_layout.h"
     39 #include "ui/views/layout/layout_constants.h"
     40 #include "ui/views/widget/widget.h"
     41 
     42 namespace {
     43 
     44 enum DenialComboboxIndexes {
     45   INDEX_NOPE = 0,
     46   INDEX_NEVER_TRANSLATE_LANGUAGE = 2,
     47   INDEX_NEVER_TRANSLATE_SITE = 4,
     48 };
     49 
     50 views::LabelButton* CreateLabelButton(views::ButtonListener* listener,
     51                                       const base::string16& label,
     52                                       int id) {
     53   views::LabelButton* button = new views::LabelButton(listener, label);
     54   button->set_id(id);
     55   button->SetStyle(views::Button::STYLE_BUTTON);
     56   return button;
     57 }
     58 
     59 views::Link* CreateLink(views::LinkListener* listener,
     60                         int resource_id,
     61                         int id) {
     62   views::Link* link = new views::Link(
     63       l10n_util::GetStringUTF16(resource_id));
     64   link->set_listener(listener);
     65   link->set_id(id);
     66   return link;
     67 }
     68 
     69 }  // namespace
     70 
     71 // static
     72 TranslateBubbleView* TranslateBubbleView::translate_bubble_view_ = NULL;
     73 
     74 TranslateBubbleView::~TranslateBubbleView() {
     75   // A child view could refer to a model which is owned by this class when
     76   // the child view is destructed. For example, |source_language_combobx_model_|
     77   // is referred by Combobox's destructor. Before destroying the models,
     78   // removing the child views is needed.
     79   RemoveAllChildViews(true);
     80 }
     81 
     82 // static
     83 void TranslateBubbleView::ShowBubble(views::View* anchor_view,
     84                                      content::WebContents* web_contents,
     85                                      translate::TranslateStep step,
     86                                      TranslateErrors::Type error_type) {
     87   if (IsShowing()) {
     88     // When the user reads the advanced setting panel, the bubble should not be
     89     // changed because he/she is focusing on the bubble.
     90     if (translate_bubble_view_->web_contents() == web_contents &&
     91         translate_bubble_view_->model()->GetViewState() ==
     92         TranslateBubbleModel::VIEW_STATE_ADVANCED) {
     93       return;
     94     }
     95     if (step != translate::TRANSLATE_STEP_TRANSLATE_ERROR) {
     96       TranslateBubbleModel::ViewState state =
     97           TranslateBubbleModelImpl::TranslateStepToViewState(step);
     98       translate_bubble_view_->SwitchView(state);
     99     } else {
    100       translate_bubble_view_->SwitchToErrorView(error_type);
    101     }
    102     return;
    103   }
    104 
    105   std::string source_language;
    106   std::string target_language;
    107   ChromeTranslateClient::GetTranslateLanguages(
    108       web_contents, &source_language, &target_language);
    109 
    110   scoped_ptr<TranslateUIDelegate> ui_delegate(new TranslateUIDelegate(
    111       ChromeTranslateClient::GetManagerFromWebContents(web_contents)
    112           ->GetWeakPtr(),
    113       source_language,
    114       target_language));
    115   scoped_ptr<TranslateBubbleModel> model(
    116       new TranslateBubbleModelImpl(step, ui_delegate.Pass()));
    117   TranslateBubbleView* view = new TranslateBubbleView(anchor_view,
    118                                                       model.Pass(),
    119                                                       error_type,
    120                                                       web_contents);
    121   views::BubbleDelegateView::CreateBubble(view)->Show();
    122 }
    123 
    124 // static
    125 bool TranslateBubbleView::IsShowing() {
    126   return translate_bubble_view_ != NULL;
    127 }
    128 
    129 // static
    130 TranslateBubbleView* TranslateBubbleView::GetCurrentBubble() {
    131   return translate_bubble_view_;
    132 }
    133 
    134 void TranslateBubbleView::Init() {
    135   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical,
    136                                         0, 0, 0));
    137 
    138   before_translate_view_ = CreateViewBeforeTranslate();
    139   translating_view_ = CreateViewTranslating();
    140   after_translate_view_ = CreateViewAfterTranslate();
    141   error_view_ = CreateViewError();
    142   advanced_view_ = CreateViewAdvanced();
    143 
    144   AddChildView(before_translate_view_);
    145   AddChildView(translating_view_);
    146   AddChildView(after_translate_view_);
    147   AddChildView(error_view_);
    148   AddChildView(advanced_view_);
    149 
    150   AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE));
    151 
    152   UpdateChildVisibilities();
    153 
    154   if (model_->GetViewState() == TranslateBubbleModel::VIEW_STATE_ERROR)
    155     model_->ShowError(error_type_);
    156 }
    157 
    158 void TranslateBubbleView::ButtonPressed(views::Button* sender,
    159                                         const ui::Event& event) {
    160   HandleButtonPressed(static_cast<ButtonID>(sender->id()));
    161 }
    162 
    163 void TranslateBubbleView::WindowClosing() {
    164   // The operations for |model_| are valid only when a WebContents is alive.
    165   // TODO(hajimehoshi): TranslateBubbleViewModel(Impl) should not hold a
    166   // WebContents as a member variable because the WebContents might be destroyed
    167   // while the TranslateBubbleViewModel(Impl) is still alive. Instead,
    168   // TranslateBubbleViewModel should take a reference of a WebContents at each
    169   // method. (crbug/320497)
    170   if (!translate_executed_ && web_contents())
    171     model_->TranslationDeclined(denial_button_clicked_);
    172 
    173   // We have to reset |translate_bubble_view_| here, not in our destructor,
    174   // because we'll be destroyed asynchronously and the shown state will be
    175   // checked before then.
    176   DCHECK_EQ(translate_bubble_view_, this);
    177   translate_bubble_view_ = NULL;
    178 }
    179 
    180 bool TranslateBubbleView::AcceleratorPressed(
    181     const ui::Accelerator& accelerator) {
    182   switch (model_->GetViewState()) {
    183     case TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE: {
    184       if (accelerator.key_code() == ui::VKEY_RETURN) {
    185         HandleButtonPressed(BUTTON_ID_TRANSLATE);
    186         return true;
    187       }
    188       break;
    189     }
    190     case TranslateBubbleModel::VIEW_STATE_TRANSLATING:
    191       break;
    192     case TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE: {
    193       if (accelerator.key_code() == ui::VKEY_RETURN) {
    194         HandleButtonPressed(BUTTON_ID_SHOW_ORIGINAL);
    195         return true;
    196       }
    197       break;
    198     }
    199     case TranslateBubbleModel::VIEW_STATE_ERROR:
    200       break;
    201     case TranslateBubbleModel::VIEW_STATE_ADVANCED: {
    202       if (accelerator.key_code() == ui::VKEY_RETURN) {
    203         HandleButtonPressed(BUTTON_ID_DONE);
    204         return true;
    205       }
    206       break;
    207     }
    208   }
    209   return BubbleDelegateView::AcceleratorPressed(accelerator);
    210 }
    211 
    212 gfx::Size TranslateBubbleView::GetPreferredSize() const {
    213   int width = 0;
    214   for (int i = 0; i < child_count(); i++) {
    215     const views::View* child = child_at(i);
    216     width = std::max(width, child->GetPreferredSize().width());
    217   }
    218   int height = GetCurrentView()->GetPreferredSize().height();
    219   return gfx::Size(width, height);
    220 }
    221 
    222 void TranslateBubbleView::OnPerformAction(views::Combobox* combobox) {
    223   HandleComboboxPerformAction(static_cast<ComboboxID>(combobox->id()));
    224 }
    225 
    226 void TranslateBubbleView::LinkClicked(views::Link* source, int event_flags) {
    227   HandleLinkClicked(static_cast<LinkID>(source->id()));
    228 }
    229 
    230 void TranslateBubbleView::WebContentsDestroyed() {
    231   GetWidget()->CloseNow();
    232 }
    233 
    234 TranslateBubbleModel::ViewState TranslateBubbleView::GetViewState() const {
    235   return model_->GetViewState();
    236 }
    237 
    238 TranslateBubbleView::TranslateBubbleView(
    239     views::View* anchor_view,
    240     scoped_ptr<TranslateBubbleModel> model,
    241     TranslateErrors::Type error_type,
    242     content::WebContents* web_contents)
    243     : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
    244       WebContentsObserver(web_contents),
    245       before_translate_view_(NULL),
    246       translating_view_(NULL),
    247       after_translate_view_(NULL),
    248       error_view_(NULL),
    249       advanced_view_(NULL),
    250       denial_combobox_(NULL),
    251       source_language_combobox_(NULL),
    252       target_language_combobox_(NULL),
    253       always_translate_checkbox_(NULL),
    254       advanced_cancel_button_(NULL),
    255       advanced_done_button_(NULL),
    256       model_(model.Pass()),
    257       error_type_(error_type),
    258       is_in_incognito_window_(
    259           web_contents ?
    260           web_contents->GetBrowserContext()->IsOffTheRecord() : false),
    261       translate_executed_(false),
    262       denial_button_clicked_(false) {
    263   if (model_->GetViewState() !=
    264       TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE) {
    265     translate_executed_ = true;
    266   }
    267 
    268   set_margins(gfx::Insets(views::kPanelVertMargin, views::kPanelHorizMargin,
    269                           views::kPanelVertMargin, views::kPanelHorizMargin));
    270 
    271   translate_bubble_view_ = this;
    272 }
    273 
    274 views::View* TranslateBubbleView::GetCurrentView() const {
    275   switch (model_->GetViewState()) {
    276     case TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE:
    277       return before_translate_view_;
    278     case TranslateBubbleModel::VIEW_STATE_TRANSLATING:
    279       return translating_view_;
    280     case TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE:
    281       return after_translate_view_;
    282     case TranslateBubbleModel::VIEW_STATE_ERROR:
    283       return error_view_;
    284     case TranslateBubbleModel::VIEW_STATE_ADVANCED:
    285       return advanced_view_;
    286   }
    287   NOTREACHED();
    288   return NULL;
    289 }
    290 
    291 void TranslateBubbleView::HandleButtonPressed(
    292     TranslateBubbleView::ButtonID sender_id) {
    293   switch (sender_id) {
    294     case BUTTON_ID_TRANSLATE: {
    295       translate_executed_ = true;
    296       model_->Translate();
    297       break;
    298     }
    299     case BUTTON_ID_DONE: {
    300       if (always_translate_checkbox_)
    301         model_->SetAlwaysTranslate(always_translate_checkbox_->checked());
    302       if (model_->IsPageTranslatedInCurrentLanguages()) {
    303         model_->GoBackFromAdvanced();
    304         UpdateChildVisibilities();
    305         SizeToContents();
    306       } else {
    307         translate_executed_ = true;
    308         model_->Translate();
    309         SwitchView(TranslateBubbleModel::VIEW_STATE_TRANSLATING);
    310       }
    311       break;
    312     }
    313     case BUTTON_ID_CANCEL: {
    314       model_->GoBackFromAdvanced();
    315       UpdateChildVisibilities();
    316       SizeToContents();
    317       break;
    318     }
    319     case BUTTON_ID_TRY_AGAIN: {
    320       translate_executed_ = true;
    321       model_->Translate();
    322       break;
    323     }
    324     case BUTTON_ID_SHOW_ORIGINAL: {
    325       model_->RevertTranslation();
    326       GetWidget()->Close();
    327       break;
    328     }
    329     case BUTTON_ID_ALWAYS_TRANSLATE: {
    330       // Do nothing. The state of the checkbox affects only when the 'Done'
    331       // button is pressed.
    332       break;
    333     }
    334   }
    335 }
    336 
    337 void TranslateBubbleView::HandleLinkClicked(
    338     TranslateBubbleView::LinkID sender_id) {
    339   switch (sender_id) {
    340     case LINK_ID_ADVANCED: {
    341       SwitchView(TranslateBubbleModel::VIEW_STATE_ADVANCED);
    342       break;
    343     }
    344     case LINK_ID_LANGUAGE_SETTINGS: {
    345       GURL url = chrome::GetSettingsUrl(chrome::kLanguageOptionsSubPage);
    346       web_contents()->OpenURL(content::OpenURLParams(
    347           url,
    348           content::Referrer(),
    349           NEW_FOREGROUND_TAB,
    350           content::PAGE_TRANSITION_LINK,
    351           false));
    352       break;
    353     }
    354   }
    355 }
    356 
    357 void TranslateBubbleView::HandleComboboxPerformAction(
    358     TranslateBubbleView::ComboboxID sender_id) {
    359   switch (sender_id) {
    360     case COMBOBOX_ID_DENIAL: {
    361       denial_button_clicked_ = true;
    362       int index = denial_combobox_->selected_index();
    363       switch (index) {
    364         case INDEX_NOPE:
    365           break;
    366         case INDEX_NEVER_TRANSLATE_LANGUAGE:
    367           model_->SetNeverTranslateLanguage(true);
    368           break;
    369         case INDEX_NEVER_TRANSLATE_SITE:
    370           model_->SetNeverTranslateSite(true);
    371           break;
    372         default:
    373           NOTREACHED();
    374           break;
    375       }
    376       GetWidget()->Close();
    377       break;
    378     }
    379     case COMBOBOX_ID_SOURCE_LANGUAGE: {
    380       if (model_->GetOriginalLanguageIndex() ==
    381           source_language_combobox_->selected_index()) {
    382         break;
    383       }
    384       model_->UpdateOriginalLanguageIndex(
    385           source_language_combobox_->selected_index());
    386       UpdateAdvancedView();
    387       break;
    388     }
    389     case COMBOBOX_ID_TARGET_LANGUAGE: {
    390       if (model_->GetTargetLanguageIndex() ==
    391           target_language_combobox_->selected_index()) {
    392         break;
    393       }
    394       model_->UpdateTargetLanguageIndex(
    395           target_language_combobox_->selected_index());
    396       UpdateAdvancedView();
    397       break;
    398     }
    399   }
    400 }
    401 
    402 void TranslateBubbleView::UpdateChildVisibilities() {
    403   for (int i = 0; i < child_count(); i++) {
    404     views::View* view = child_at(i);
    405     view->SetVisible(view == GetCurrentView());
    406   }
    407 }
    408 
    409 views::View* TranslateBubbleView::CreateViewBeforeTranslate() {
    410   views::Label* message_label = new views::Label(
    411       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_BEFORE_TRANSLATE));
    412 
    413   base::string16 original_language_name =
    414       model_->GetLanguageNameAt(model_->GetOriginalLanguageIndex());
    415 
    416   std::vector<base::string16> items;
    417   items.push_back(l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_DENY));
    418   items.push_back(base::string16());
    419   items.push_back(l10n_util::GetStringFUTF16(
    420       IDS_TRANSLATE_BUBBLE_NEVER_TRANSLATE_LANG,
    421       original_language_name));
    422   items.push_back(base::string16());
    423   items.push_back(l10n_util::GetStringUTF16(
    424       IDS_TRANSLATE_BUBBLE_NEVER_TRANSLATE_SITE));
    425 
    426   denial_combobox_model_.reset(new ui::SimpleComboboxModel(items));
    427   denial_combobox_ = new views::Combobox(denial_combobox_model_.get());
    428   denial_combobox_->set_id(COMBOBOX_ID_DENIAL);
    429   denial_combobox_->set_listener(this);
    430   denial_combobox_->SetStyle(views::Combobox::STYLE_ACTION);
    431 
    432   views::View* view = new views::View();
    433   views::GridLayout* layout = new views::GridLayout(view);
    434   view->SetLayoutManager(layout);
    435 
    436   using views::GridLayout;
    437 
    438   enum {
    439     COLUMN_SET_ID_MESSAGE,
    440     COLUMN_SET_ID_CONTENT,
    441   };
    442 
    443   views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_MESSAGE);
    444   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    445                 0, GridLayout::USE_PREF, 0, 0);
    446   cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
    447   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    448                 0, GridLayout::USE_PREF, 0, 0);
    449   cs->AddPaddingColumn(1, 0);
    450 
    451   cs = layout->AddColumnSet(COLUMN_SET_ID_CONTENT);
    452   cs->AddPaddingColumn(1, 0);
    453   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    454                 0, GridLayout::USE_PREF, 0, 0);
    455   cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
    456   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    457                 0, GridLayout::USE_PREF, 0, 0);
    458 
    459   layout->StartRow(0, COLUMN_SET_ID_MESSAGE);
    460   layout->AddView(message_label);
    461   layout->AddView(CreateLink(this,
    462                              IDS_TRANSLATE_BUBBLE_ADVANCED,
    463                              LINK_ID_ADVANCED));
    464 
    465   layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
    466 
    467   layout->StartRow(0, COLUMN_SET_ID_CONTENT);
    468   layout->AddView(CreateLabelButton(
    469       this,
    470       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ACCEPT),
    471       BUTTON_ID_TRANSLATE));
    472   layout->AddView(denial_combobox_);
    473 
    474   return view;
    475 }
    476 
    477 views::View* TranslateBubbleView::CreateViewTranslating() {
    478   base::string16 target_language_name =
    479       model_->GetLanguageNameAt(model_->GetTargetLanguageIndex());
    480   views::Label* label = new views::Label(
    481       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRANSLATING));
    482 
    483   views::View* view = new views::View();
    484   views::GridLayout* layout = new views::GridLayout(view);
    485   view->SetLayoutManager(layout);
    486 
    487   using views::GridLayout;
    488 
    489   enum {
    490     COLUMN_SET_ID_MESSAGE,
    491     COLUMN_SET_ID_CONTENT,
    492   };
    493 
    494   views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_MESSAGE);
    495   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    496                 0, views::GridLayout::USE_PREF, 0, 0);
    497   cs->AddPaddingColumn(1, 0);
    498 
    499   cs = layout->AddColumnSet(COLUMN_SET_ID_CONTENT);
    500   cs->AddPaddingColumn(1, 0);
    501   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    502                 0, GridLayout::USE_PREF, 0, 0);
    503 
    504   layout->StartRow(0, COLUMN_SET_ID_MESSAGE);
    505   layout->AddView(label);
    506 
    507   layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
    508 
    509   layout->StartRow(0, COLUMN_SET_ID_CONTENT);
    510   views::LabelButton* revert_button = CreateLabelButton(
    511       this,
    512       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_REVERT),
    513       BUTTON_ID_SHOW_ORIGINAL);
    514   revert_button->SetEnabled(false);
    515   layout->AddView(revert_button);
    516 
    517   return view;
    518 }
    519 
    520 views::View* TranslateBubbleView::CreateViewAfterTranslate() {
    521   views::Label* label = new views::Label(
    522       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRANSLATED));
    523 
    524   views::View* view = new views::View();
    525   views::GridLayout* layout = new views::GridLayout(view);
    526   view->SetLayoutManager(layout);
    527 
    528   using views::GridLayout;
    529 
    530   enum {
    531     COLUMN_SET_ID_MESSAGE,
    532     COLUMN_SET_ID_CONTENT,
    533   };
    534 
    535   views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_MESSAGE);
    536   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    537                 0, views::GridLayout::USE_PREF, 0, 0);
    538   cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
    539   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    540                 0, views::GridLayout::USE_PREF, 0, 0);
    541   cs->AddPaddingColumn(1, 0);
    542 
    543   cs = layout->AddColumnSet(COLUMN_SET_ID_CONTENT);
    544   cs->AddPaddingColumn(1, 0);
    545   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    546                 0, GridLayout::USE_PREF, 0, 0);
    547 
    548   layout->StartRow(0, COLUMN_SET_ID_MESSAGE);
    549   layout->AddView(label);
    550   layout->AddView(CreateLink(this,
    551                              IDS_TRANSLATE_BUBBLE_ADVANCED,
    552                              LINK_ID_ADVANCED));
    553 
    554   layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
    555 
    556   layout->StartRow(0, COLUMN_SET_ID_CONTENT);
    557   layout->AddView(CreateLabelButton(
    558       this,
    559       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_REVERT),
    560       BUTTON_ID_SHOW_ORIGINAL));
    561 
    562   return view;
    563 }
    564 
    565 views::View* TranslateBubbleView::CreateViewError() {
    566   views::Label* label = new views::Label(
    567       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE));
    568 
    569   views::View* view = new views::View();
    570   views::GridLayout* layout = new views::GridLayout(view);
    571   view->SetLayoutManager(layout);
    572 
    573   using views::GridLayout;
    574 
    575   enum {
    576     COLUMN_SET_ID_MESSAGE,
    577     COLUMN_SET_ID_CONTENT,
    578   };
    579 
    580   views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_MESSAGE);
    581   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    582                 0, GridLayout::USE_PREF, 0, 0);
    583   cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
    584   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    585                 0, GridLayout::USE_PREF, 0, 0);
    586   cs->AddPaddingColumn(1, 0);
    587 
    588   cs = layout->AddColumnSet(COLUMN_SET_ID_CONTENT);
    589   cs->AddPaddingColumn(1, 0);
    590   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    591                 0, GridLayout::USE_PREF, 0, 0);
    592 
    593   layout->StartRow(0, COLUMN_SET_ID_MESSAGE);
    594   layout->AddView(label);
    595   layout->AddView(CreateLink(this,
    596                              IDS_TRANSLATE_BUBBLE_ADVANCED,
    597                              LINK_ID_ADVANCED));
    598 
    599   layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
    600 
    601   layout->StartRow(0, COLUMN_SET_ID_CONTENT);
    602   layout->AddView(CreateLabelButton(
    603       this,
    604       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRY_AGAIN),
    605       BUTTON_ID_TRY_AGAIN));
    606 
    607   return view;
    608 }
    609 
    610 // TODO(hajimehoshi): Revice this later to show a specific message for each
    611 // error. (crbug/307350)
    612 views::View* TranslateBubbleView::CreateViewAdvanced() {
    613   views::Label* source_language_label = new views::Label(
    614       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_PAGE_LANGUAGE));
    615 
    616   views::Label* target_language_label = new views::Label(
    617       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRANSLATION_LANGUAGE));
    618 
    619   int source_default_index = model_->GetOriginalLanguageIndex();
    620   source_language_combobox_model_.reset(
    621       new LanguageComboboxModel(source_default_index, model_.get()));
    622   source_language_combobox_ =
    623       new views::Combobox(source_language_combobox_model_.get());
    624 
    625   source_language_combobox_->set_id(COMBOBOX_ID_SOURCE_LANGUAGE);
    626   source_language_combobox_->set_listener(this);
    627 
    628   int target_default_index = model_->GetTargetLanguageIndex();
    629   target_language_combobox_model_.reset(
    630       new LanguageComboboxModel(target_default_index, model_.get()));
    631   target_language_combobox_ =
    632       new views::Combobox(target_language_combobox_model_.get());
    633 
    634   target_language_combobox_->set_id(COMBOBOX_ID_TARGET_LANGUAGE);
    635   target_language_combobox_->set_listener(this);
    636 
    637   // In an incognito window, "Always translate" checkbox shouldn't be shown.
    638   if (!is_in_incognito_window_) {
    639     always_translate_checkbox_ = new views::Checkbox(base::string16());
    640     always_translate_checkbox_->set_id(BUTTON_ID_ALWAYS_TRANSLATE);
    641     always_translate_checkbox_->set_listener(this);
    642   }
    643 
    644   views::View* view = new views::View();
    645   views::GridLayout* layout = new views::GridLayout(view);
    646   view->SetLayoutManager(layout);
    647 
    648   using views::GridLayout;
    649 
    650   enum {
    651     COLUMN_SET_ID_LANGUAGES,
    652     COLUMN_SET_ID_BUTTONS,
    653   };
    654 
    655   views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_ID_LANGUAGES);
    656   cs->AddColumn(GridLayout::TRAILING, GridLayout::CENTER,
    657                 0, GridLayout::USE_PREF, 0, 0);
    658   cs->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
    659   cs->AddColumn(GridLayout::FILL, GridLayout::CENTER,
    660                 0, GridLayout::USE_PREF, 0, 0);
    661   cs->AddPaddingColumn(1, 0);
    662 
    663   cs = layout->AddColumnSet(COLUMN_SET_ID_BUTTONS);
    664   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    665                 0, GridLayout::USE_PREF, 0, 0);
    666   cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing);
    667   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    668                 0, GridLayout::USE_PREF, 0, 0);
    669   cs->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
    670   cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
    671                 0, GridLayout::USE_PREF, 0, 0);
    672 
    673   layout->StartRow(0, COLUMN_SET_ID_LANGUAGES);
    674   layout->AddView(source_language_label);
    675   layout->AddView(source_language_combobox_);
    676 
    677   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
    678 
    679   layout->StartRow(0, COLUMN_SET_ID_LANGUAGES);
    680   layout->AddView(target_language_label);
    681   layout->AddView(target_language_combobox_);
    682 
    683   if (!is_in_incognito_window_) {
    684     layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
    685     layout->StartRow(0, COLUMN_SET_ID_LANGUAGES);
    686     layout->SkipColumns(1);
    687     layout->AddView(always_translate_checkbox_);
    688   }
    689 
    690   layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
    691 
    692   layout->StartRow(0, COLUMN_SET_ID_BUTTONS);
    693   layout->AddView(CreateLink(this,
    694                              IDS_TRANSLATE_BUBBLE_LANGUAGE_SETTINGS,
    695                              LINK_ID_LANGUAGE_SETTINGS));
    696   advanced_done_button_ = CreateLabelButton(
    697       this, l10n_util::GetStringUTF16(IDS_DONE), BUTTON_ID_DONE);
    698   advanced_done_button_->SetIsDefault(true);
    699   advanced_cancel_button_ = CreateLabelButton(
    700       this, l10n_util::GetStringUTF16(IDS_CANCEL), BUTTON_ID_CANCEL);
    701   layout->AddView(advanced_done_button_);
    702   layout->AddView(advanced_cancel_button_);
    703 
    704   UpdateAdvancedView();
    705 
    706   return view;
    707 }
    708 
    709 void TranslateBubbleView::SwitchView(
    710     TranslateBubbleModel::ViewState view_state) {
    711   if (model_->GetViewState() == view_state)
    712     return;
    713 
    714   model_->SetViewState(view_state);
    715   UpdateChildVisibilities();
    716   if (view_state == TranslateBubbleModel::VIEW_STATE_ADVANCED)
    717     UpdateAdvancedView();
    718   SizeToContents();
    719 }
    720 
    721 void TranslateBubbleView::SwitchToErrorView(TranslateErrors::Type error_type) {
    722   SwitchView(TranslateBubbleModel::VIEW_STATE_ERROR);
    723   error_type_ = error_type;
    724   model_->ShowError(error_type);
    725 }
    726 
    727 void TranslateBubbleView::UpdateAdvancedView() {
    728   DCHECK(source_language_combobox_);
    729   DCHECK(target_language_combobox_);
    730   DCHECK(advanced_done_button_);
    731 
    732   base::string16 source_language_name =
    733       model_->GetLanguageNameAt(model_->GetOriginalLanguageIndex());
    734   base::string16 target_language_name =
    735       model_->GetLanguageNameAt(model_->GetTargetLanguageIndex());
    736 
    737   // "Always translate" checkbox doesn't exist in an incognito window.
    738   if (always_translate_checkbox_) {
    739     always_translate_checkbox_->SetText(
    740         l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ALWAYS));
    741     always_translate_checkbox_->SetChecked(
    742         model_->ShouldAlwaysTranslate());
    743   }
    744 
    745   base::string16 label;
    746   if (model_->IsPageTranslatedInCurrentLanguages())
    747     label = l10n_util::GetStringUTF16(IDS_DONE);
    748   else
    749     label = l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_ACCEPT);
    750   advanced_done_button_->SetText(label);
    751 }
    752