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