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