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