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
LanguageSwitchMenu()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
InitLanguageMenu()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
GetCurrentLocaleName() const70 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
SetFirstLevelMenuWidth(int width)78 void LanguageSwitchMenu::SetFirstLevelMenuWidth(int width) {
79 DCHECK(menu_ != NULL);
80 menu_->SetMinimumWidth(width);
81 }
82
83 // static
SwitchLanguage(const std::string & locale)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
LoadFontsForCurrentLocale()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
SwitchLanguageAndEnableKeyboardLayouts(const std::string & locale)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
RunMenu(views::View * source,const gfx::Point & pt)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
IsCommandIdChecked(int command_id) const168 bool LanguageSwitchMenu::IsCommandIdChecked(int command_id) const {
169 return false;
170 }
171
IsCommandIdEnabled(int command_id) const172 bool LanguageSwitchMenu::IsCommandIdEnabled(int command_id) const {
173 return true;
174 }
175
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)176 bool LanguageSwitchMenu::GetAcceleratorForCommandId(
177 int command_id, ui::Accelerator* accelerator) {
178 return false;
179 }
180
ExecuteCommand(int command_id)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