blob: d83e7a2c7676b2b3457bd71893054d2c336a2249 [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/gtk/translate_infobars.h"
#include <string>
#include <vector>
#include "app/l10n_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/gtk/menu_gtk.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/translate/translate_infobars_delegates.h"
#include "chrome/browser/translate/options_menu_model.h"
#include "chrome/common/notification_service.h"
#include "grit/app_resources.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
namespace {
// Reorders the widgets in the NULL terminated array |widgets| that are all
// children of |box|.
void ReorderWidgetsTo(GtkWidget* box, GtkWidget** widgets) {
for (int count = 0; *widgets != NULL; widgets++) {
gtk_box_reorder_child(GTK_BOX(box), *widgets, count++);
gtk_widget_show_all(*widgets);
}
}
// Creates a combobox set up to display text from a list of language codes
// (translating the codes into the display string).
GtkWidget* BuildLanguageComboboxFrom(
TranslateInfoBarDelegate* delegate,
const std::vector<std::string>& languages) {
GtkListStore* model = gtk_list_store_new(1, G_TYPE_STRING);
for (std::vector<std::string>::const_iterator iter = languages.begin();
iter != languages.end(); ++iter) {
GtkTreeIter tree_iter;
std::string name = UTF16ToUTF8(delegate->GetDisplayNameForLocale(*iter));
gtk_list_store_append(model, &tree_iter);
gtk_list_store_set(model, &tree_iter, 0, name.c_str(), -1);
}
GtkWidget* combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
g_object_unref(model);
GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, TRUE);
gtk_cell_layout_set_attributes(
GTK_CELL_LAYOUT(combobox), renderer, "text", 0, NULL);
return combobox;
}
// Builds a button with an arrow in it to emulate the menu-button style from
// the windows version.
GtkWidget* BuildOptionsMenuButton() {
GtkWidget* button = gtk_button_new();
GtkWidget* former_child = gtk_bin_get_child(GTK_BIN(button));
if (former_child)
gtk_container_remove(GTK_CONTAINER(button), former_child);
GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
GtkWidget* label = gtk_label_new(
l10n_util::GetStringUTF8(IDS_TRANSLATE_INFOBAR_OPTIONS).c_str());
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
GtkWidget* arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
gtk_box_pack_start(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(button), hbox);
return button;
}
} // namespace
TranslateInfoBar::TranslateInfoBar(TranslateInfoBarDelegate* delegate)
: InfoBar(delegate),
swapped_language_placeholders_(false) {
BuildWidgets();
// Register for PAGE_TRANSLATED notification.
notification_registrar_.Add(this, NotificationType::PAGE_TRANSLATED,
Source<TabContents>(GetDelegate()->tab_contents()));
}
TranslateInfoBar::~TranslateInfoBar() {
}
void TranslateInfoBar::Observe(NotificationType type,
const NotificationSource& source, const NotificationDetails& details) {
if (type.value == NotificationType::PAGE_TRANSLATED) {
TabContents* tab = Source<TabContents>(source).ptr();
if (tab != GetDelegate()->tab_contents())
return;
UpdateState(TranslateInfoBarDelegate::kAfterTranslate);
} else {
InfoBar::Observe(type, source, details);
}
}
bool TranslateInfoBar::IsCommandIdChecked(int command_id) const {
TranslateInfoBarDelegate* translate_delegate = GetDelegate();
switch (command_id) {
case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_LANG :
return translate_delegate->IsLanguageBlacklisted();
case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_SITE :
return translate_delegate->IsSiteBlacklisted();
case IDC_TRANSLATE_OPTIONS_ALWAYS :
return translate_delegate->ShouldAlwaysTranslate();
default:
NOTREACHED() << "Invalid command_id from menu";
break;
}
return false;
}
bool TranslateInfoBar::IsCommandIdEnabled(int command_id) const {
return true;
}
bool TranslateInfoBar::GetAcceleratorForCommandId(int command_id,
menus::Accelerator* accelerator) {
return false;
}
void TranslateInfoBar::ExecuteCommand(int command_id) {
switch (command_id) {
case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_LANG:
GetDelegate()->ToggleLanguageBlacklist();
break;
case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_SITE:
GetDelegate()->ToggleSiteBlacklist();
break;
case IDC_TRANSLATE_OPTIONS_ALWAYS:
GetDelegate()->ToggleAlwaysTranslate();
break;
case IDC_TRANSLATE_OPTIONS_ABOUT: {
TabContents* tab_contents = GetDelegate()->tab_contents();
if (tab_contents) {
string16 url = l10n_util::GetStringUTF16(
IDS_ABOUT_GOOGLE_TRANSLATE_URL);
tab_contents->OpenURL(GURL(url), GURL(), NEW_FOREGROUND_TAB,
PageTransition::LINK);
}
break;
}
default:
NOTREACHED() << "Invalid command id from menu.";
break;
}
}
void TranslateInfoBar::BuildWidgets() {
translate_box_ = gtk_hbox_new(FALSE, gtk_util::kControlSpacing);
gtk_widget_set_no_show_all(translate_box_, TRUE);
// Add all labels to the translate_box_. Unlike views or cocoa, there's no
// concept of manual layout in GTK, so we'll manually reorder these widgets
// inside |translate_box_| and selectively show and hide them.
label_1_ = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(translate_box_), label_1_, FALSE, FALSE, 0);
label_2_ = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(translate_box_), label_2_, FALSE, FALSE, 0);
label_3_ = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(translate_box_), label_3_, FALSE, FALSE, 0);
translating_label_ = gtk_label_new(
l10n_util::GetStringUTF8(IDS_TRANSLATE_INFOBAR_TRANSLATING).c_str());
gtk_box_pack_start(GTK_BOX(translate_box_), translating_label_,
FALSE, FALSE, 0);
accept_button_ = gtk_button_new_with_label(
l10n_util::GetStringUTF8(IDS_TRANSLATE_INFOBAR_ACCEPT).c_str());
g_signal_connect(accept_button_, "clicked",
G_CALLBACK(&OnAcceptPressedThunk), this);
accept_button_vbox_ = gtk_util::CenterWidgetInHBox(
translate_box_, accept_button_, false, 0);
deny_button_ = gtk_button_new_with_label(
l10n_util::GetStringUTF8(IDS_TRANSLATE_INFOBAR_DENY).c_str());
g_signal_connect(deny_button_, "clicked",
G_CALLBACK(&OnDenyPressedThunk), this);
deny_button_vbox_ = gtk_util::CenterWidgetInHBox(
translate_box_, deny_button_, false, 0);
std::vector<std::string> orig_languages;
GetDelegate()->GetAvailableOriginalLanguages(&orig_languages);
original_language_combobox_ = BuildLanguageComboboxFrom(GetDelegate(),
orig_languages);
g_signal_connect(original_language_combobox_, "changed",
G_CALLBACK(&OnOriginalModifiedThunk), this);
original_language_combobox_vbox_ = gtk_util::CenterWidgetInHBox(
translate_box_, original_language_combobox_, false, 0);
std::vector<std::string> target_languages;
GetDelegate()->GetAvailableTargetLanguages(&target_languages);
target_language_combobox_ = BuildLanguageComboboxFrom(GetDelegate(),
target_languages);
g_signal_connect(target_language_combobox_, "changed",
G_CALLBACK(&OnTargetModifiedThunk), this);
target_language_combobox_vbox_ = gtk_util::CenterWidgetInHBox(
translate_box_, target_language_combobox_, false, 0);
gtk_box_pack_start(GTK_BOX(hbox_), translate_box_, FALSE, FALSE, 0);
// The options button sits outside the translate_box so that it can be end
// packed in hbox_.
options_menu_button_ = BuildOptionsMenuButton();
g_signal_connect(options_menu_button_, "clicked",
G_CALLBACK(&OnOptionsClickedThunk), this);
gtk_widget_show_all(options_menu_button_);
gtk_util::CenterWidgetInHBox(
hbox_, options_menu_button_, true, 0);
UpdateState(GetDelegate()->state());
gtk_widget_show_all(border_bin_.get());
}
void TranslateInfoBar::UpdateState(
TranslateInfoBarDelegate::TranslateState new_state) {
// Show the box...
gtk_widget_show(translate_box_);
// ...but hide all children of the translate box. They will be selectively
// unhidden and reordered based on the current translate state.
gtk_container_foreach(
GTK_CONTAINER(translate_box_),
reinterpret_cast<void (*)(GtkWidget*, void*)>(gtk_widget_hide_all), NULL);
switch (new_state) {
case TranslateInfoBarDelegate::kBeforeTranslate: {
SetLabels();
GtkWidget* before_state[] = {
label_1_,
original_language_combobox_vbox_,
label_2_,
accept_button_vbox_,
deny_button_vbox_,
NULL
};
ReorderWidgetsTo(translate_box_, before_state);
gtk_combo_box_set_active(GTK_COMBO_BOX(original_language_combobox_),
GetDelegate()->original_lang_index());
break;
}
case TranslateInfoBarDelegate::kTranslating: {
gtk_widget_show(translating_label_);
break;
}
case TranslateInfoBarDelegate::kAfterTranslate: {
SetLabels();
GtkWidget* first_button_box =
swapped_language_placeholders_ ?
target_language_combobox_vbox_ : original_language_combobox_vbox_;
GtkWidget* second_button_box =
swapped_language_placeholders_ ?
original_language_combobox_vbox_ : target_language_combobox_vbox_;
GtkWidget* after_state[] = {
label_1_,
first_button_box,
label_2_,
second_button_box,
label_3_,
NULL
};
ReorderWidgetsTo(translate_box_, after_state);
gtk_combo_box_set_active(GTK_COMBO_BOX(original_language_combobox_),
GetDelegate()->original_lang_index());
gtk_combo_box_set_active(GTK_COMBO_BOX(target_language_combobox_),
GetDelegate()->target_lang_index());
break;
}
default: {
NOTIMPLEMENTED() << "Received state " << new_state;
}
}
}
void TranslateInfoBar::SetLabels() {
std::vector<size_t> offsets;
string16 message_text_utf16;
GetDelegate()->GetMessageText(&message_text_utf16, &offsets,
&swapped_language_placeholders_);
string16 text = message_text_utf16.substr(0, offsets[0]);
gtk_label_set_text(GTK_LABEL(label_1_), UTF16ToUTF8(text).c_str());
text = message_text_utf16.substr(offsets[0], offsets[1] - offsets[0]);
gtk_label_set_text(GTK_LABEL(label_2_), UTF16ToUTF8(text).c_str());
if (offsets.size() == 3) {
text = message_text_utf16.substr(offsets[1], offsets[2] - offsets[1]);
gtk_label_set_text(GTK_LABEL(label_3_), UTF16ToUTF8(text).c_str());
}
}
TranslateInfoBarDelegate* TranslateInfoBar::GetDelegate() const {
return static_cast<TranslateInfoBarDelegate*>(delegate());
}
void TranslateInfoBar::LanguageModified() {
options_menu_model_.reset();
// Selecting an item from the "from language" menu in the before translate
// phase shouldn't trigger translation - http://crbug.com/36666
if (GetDelegate()->state() == TranslateInfoBarDelegate::kAfterTranslate)
GetDelegate()->Translate();
}
void TranslateInfoBar::OnOriginalModified(GtkWidget* sender) {
int index = gtk_combo_box_get_active(GTK_COMBO_BOX(sender));
if (index == GetDelegate()->original_lang_index())
return;
GetDelegate()->ModifyOriginalLanguage(index);
LanguageModified();
}
void TranslateInfoBar::OnTargetModified(GtkWidget* sender) {
int index = gtk_combo_box_get_active(GTK_COMBO_BOX(sender));
if (index == GetDelegate()->target_lang_index())
return;
GetDelegate()->ModifyTargetLanguage(index);
LanguageModified();
}
void TranslateInfoBar::OnAcceptPressed(GtkWidget* sender) {
GetDelegate()->Translate();
UpdateState(GetDelegate()->state());
}
void TranslateInfoBar::OnDenyPressed(GtkWidget* sender) {
GetDelegate()->TranslationDeclined();
RemoveInfoBar();
}
void TranslateInfoBar::OnOptionsClicked(GtkWidget* sender) {
if (!options_menu_model_.get()) {
options_menu_model_.reset(new OptionsMenuModel(this, GetDelegate()));
options_menu_menu_.reset(new MenuGtk(this, options_menu_model_.get()));
}
options_menu_menu_->Popup(sender, 1, gtk_get_current_event_time());
}
// TranslateInfoBarDelegate, InfoBarDelegate overrides: ------------------
InfoBar* TranslateInfoBarDelegate::CreateInfoBar() {
return new TranslateInfoBar(this);
}