translate_bubble_view.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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
42namespace {
43
44enum DenialComboboxIndexes {
45  INDEX_NOPE = 0,
46  INDEX_NEVER_TRANSLATE_LANGUAGE = 2,
47  INDEX_NEVER_TRANSLATE_SITE = 4,
48};
49
50views::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
59views::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
72TranslateBubbleView* TranslateBubbleView::translate_bubble_view_ = NULL;
73
74TranslateBubbleView::~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
83void 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
125bool TranslateBubbleView::IsShowing() {
126  return translate_bubble_view_ != NULL;
127}
128
129// static
130TranslateBubbleView* TranslateBubbleView::GetCurrentBubble() {
131  return translate_bubble_view_;
132}
133
134void 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
158void TranslateBubbleView::ButtonPressed(views::Button* sender,
159                                        const ui::Event& event) {
160  HandleButtonPressed(static_cast<ButtonID>(sender->id()));
161}
162
163void 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
180bool 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
212gfx::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
222void TranslateBubbleView::OnPerformAction(views::Combobox* combobox) {
223  HandleComboboxPerformAction(static_cast<ComboboxID>(combobox->id()));
224}
225
226void TranslateBubbleView::LinkClicked(views::Link* source, int event_flags) {
227  HandleLinkClicked(static_cast<LinkID>(source->id()));
228}
229
230void TranslateBubbleView::WebContentsDestroyed() {
231  GetWidget()->CloseNow();
232}
233
234TranslateBubbleModel::ViewState TranslateBubbleView::GetViewState() const {
235  return model_->GetViewState();
236}
237
238TranslateBubbleView::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
274views::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
291void 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
337void 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
357void 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
402void 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
409views::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
477views::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
520views::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
565views::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)
612views::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
709void 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
721void TranslateBubbleView::SwitchToErrorView(TranslateErrors::Type error_type) {
722  SwitchView(TranslateBubbleModel::VIEW_STATE_ERROR);
723  error_type_ = error_type;
724  model_->ShowError(error_type);
725}
726
727void 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