Home | History | Annotate | Download | only in login
      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