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