1 // Copyright (c) 2011 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/chromeos/login/language_switch_menu.h" 6 7 #include "base/i18n/rtl.h" 8 #include "base/threading/thread_restrictions.h" 9 #include "base/utf_string_conversions.h" 10 #include "chrome/browser/browser_process.h" 11 #include "chrome/browser/chromeos/cros/cros_library.h" 12 #include "chrome/browser/chromeos/input_method/input_method_util.h" 13 #include "chrome/browser/chromeos/language_preferences.h" 14 #include "chrome/browser/chromeos/login/ownership_service.h" 15 #include "chrome/browser/chromeos/login/screen_observer.h" 16 #include "chrome/browser/prefs/pref_service.h" 17 #include "chrome/common/pref_names.h" 18 #include "grit/generated_resources.h" 19 #include "grit/platform_locale_settings.h" 20 #include "ui/base/l10n/l10n_util.h" 21 #include "ui/base/resource/resource_bundle.h" 22 #include "ui/gfx/platform_font_gtk.h" 23 #include "views/controls/button/menu_button.h" 24 #include "views/widget/widget_gtk.h" 25 26 namespace { 27 28 const int kLanguageMainMenuSize = 5; 29 // TODO(glotov): need to specify the list as a part of the image customization. 30 const char kLanguagesTopped[] = "es,it,de,fr,en-US"; 31 const int kMoreLanguagesSubMenu = 200; 32 33 } // namespace 34 35 namespace chromeos { 36 37 LanguageSwitchMenu::LanguageSwitchMenu() 38 : ALLOW_THIS_IN_INITIALIZER_LIST(menu_model_(this)), 39 ALLOW_THIS_IN_INITIALIZER_LIST(menu_model_submenu_(this)), 40 menu_alignment_(views::Menu2::ALIGN_TOPRIGHT) { 41 } 42 43 void LanguageSwitchMenu::InitLanguageMenu() { 44 // Update LanguageList to contain entries in current locale. 45 language_list_.reset(new LanguageList); 46 language_list_->CopySpecifiedLanguagesUp(kLanguagesTopped); 47 48 // Clear older menu items. 49 menu_model_.Clear(); 50 menu_model_submenu_.Clear(); 51 52 // Fill menu items with updated items. 53 for (int line = 0; line != kLanguageMainMenuSize; line++) { 54 menu_model_.AddItem(line, language_list_->GetLanguageNameAt(line)); 55 } 56 menu_model_.AddSeparator(); 57 menu_model_.AddSubMenuWithStringId(kMoreLanguagesSubMenu, 58 IDS_LANGUAGES_MORE, 59 &menu_model_submenu_); 60 for (int line = kLanguageMainMenuSize; 61 line != language_list_->get_languages_count(); line++) { 62 menu_model_submenu_.AddItem( 63 line, language_list_->GetLanguageNameAt(line)); 64 } 65 66 // Initialize menu here so it appears fast when called. 67 menu_.reset(new views::Menu2(&menu_model_)); 68 } 69 70 string16 LanguageSwitchMenu::GetCurrentLocaleName() const { 71 DCHECK(g_browser_process); 72 const std::string locale = g_browser_process->GetApplicationLocale(); 73 int index = language_list_->GetIndexFromLocale(locale); 74 CHECK_NE(-1, index) << "Unknown locale: " << locale; 75 return language_list_->GetLanguageNameAt(index); 76 }; 77 78 void LanguageSwitchMenu::SetFirstLevelMenuWidth(int width) { 79 DCHECK(menu_ != NULL); 80 menu_->SetMinimumWidth(width); 81 } 82 83 // static 84 bool LanguageSwitchMenu::SwitchLanguage(const std::string& locale) { 85 DCHECK(g_browser_process); 86 if (g_browser_process->GetApplicationLocale() == locale) { 87 return false; 88 } 89 // TODO(markusheintz): Change the if condition to prefs->IsUserModifiable() 90 // once Mattias landed his pending patch. 91 if (!g_browser_process->local_state()-> 92 IsManagedPreference(prefs::kApplicationLocale)) { 93 std::string loaded_locale; 94 { 95 // Reloading resource bundle causes us to do blocking IO on UI thread. 96 // Temporarily allow it until we fix http://crosbug.com/11102 97 base::ThreadRestrictions::ScopedAllowIO allow_io; 98 // Switch the locale. 99 loaded_locale = ResourceBundle::ReloadSharedInstance(locale); 100 } 101 CHECK(!loaded_locale.empty()) << "Locale could not be found for " << locale; 102 103 LoadFontsForCurrentLocale(); 104 // The following line does not seem to affect locale anyhow. Maybe in 105 // future.. 106 g_browser_process->SetApplicationLocale(locale); 107 return true; 108 } 109 return false; 110 } 111 112 // static 113 void LanguageSwitchMenu::LoadFontsForCurrentLocale() { 114 std::string gtkrc = l10n_util::GetStringUTF8(IDS_LOCALE_GTKRC); 115 116 // Read locale-specific gtkrc. Ideally we'd discard all the previously read 117 // gtkrc information, but GTK doesn't support that. Reading the new locale's 118 // gtkrc overrides the styles from previous ones when there is a conflict, but 119 // styles that are added and not conflicted will not be overridden. So far 120 // there are no locales with such a thing; if there are then this solution 121 // will not work. 122 if (!gtkrc.empty()) 123 gtk_rc_parse_string(gtkrc.c_str()); 124 else 125 gtk_rc_parse("/etc/gtk-2.0/gtkrc"); 126 127 // Switch the font. 128 gfx::PlatformFontGtk::ReloadDefaultFont(); 129 ResourceBundle::GetSharedInstance().ReloadFonts(); 130 } 131 132 // static 133 void LanguageSwitchMenu::SwitchLanguageAndEnableKeyboardLayouts( 134 const std::string& locale) { 135 if (SwitchLanguage(locale)) { 136 // If we have switched the locale, enable the keyboard layouts that 137 // are necessary for the new locale. Change the current input method 138 // to the hardware keyboard layout since the input method currently in 139 // use may not be supported by the new locale (3rd parameter). 140 input_method::EnableInputMethods( 141 locale, input_method::kKeyboardLayoutsOnly, 142 input_method::GetHardwareInputMethodId()); 143 } 144 } 145 146 //////////////////////////////////////////////////////////////////////////////// 147 // views::ViewMenuDelegate implementation. 148 149 void LanguageSwitchMenu::RunMenu(views::View* source, const gfx::Point& pt) { 150 DCHECK(menu_ != NULL); 151 views::MenuButton* button = static_cast<views::MenuButton*>(source); 152 // We align the on left edge of the button for non RTL case. 153 gfx::Point new_pt(pt); 154 if (menu_alignment_ == views::Menu2::ALIGN_TOPLEFT) { 155 int reverse_offset = button->width() + button->menu_offset().x() * 2; 156 if (base::i18n::IsRTL()) { 157 new_pt.set_x(pt.x() + reverse_offset); 158 } else { 159 new_pt.set_x(pt.x() - reverse_offset); 160 } 161 } 162 menu_->RunMenuAt(new_pt, menu_alignment_); 163 } 164 165 //////////////////////////////////////////////////////////////////////////////// 166 // ui::SimpleMenuModel::Delegate implementation. 167 168 bool LanguageSwitchMenu::IsCommandIdChecked(int command_id) const { 169 return false; 170 } 171 172 bool LanguageSwitchMenu::IsCommandIdEnabled(int command_id) const { 173 return true; 174 } 175 176 bool LanguageSwitchMenu::GetAcceleratorForCommandId( 177 int command_id, ui::Accelerator* accelerator) { 178 return false; 179 } 180 181 void LanguageSwitchMenu::ExecuteCommand(int command_id) { 182 const std::string locale = language_list_->GetLocaleFromIndex(command_id); 183 // Here, we should enable keyboard layouts associated with the locale so 184 // that users can use those keyboard layouts on the login screen. 185 SwitchLanguageAndEnableKeyboardLayouts(locale); 186 g_browser_process->local_state()->SetString( 187 prefs::kApplicationLocale, locale); 188 g_browser_process->local_state()->ScheduleSavePersistentPrefs(); 189 InitLanguageMenu(); 190 191 // Update all view hierarchies that the locale has changed. 192 views::Widget::NotifyLocaleChanged(); 193 } 194 195 } // namespace chromeos 196