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