普通文本  |  196行  |  7 KB

// Copyright (c) 2011 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/chromeos/login/language_switch_menu.h"

#include "base/i18n/rtl.h"
#include "base/threading/thread_restrictions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/input_method/input_method_util.h"
#include "chrome/browser/chromeos/language_preferences.h"
#include "chrome/browser/chromeos/login/ownership_service.h"
#include "chrome/browser/chromeos/login/screen_observer.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/common/pref_names.h"
#include "grit/generated_resources.h"
#include "grit/platform_locale_settings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/platform_font_gtk.h"
#include "views/controls/button/menu_button.h"
#include "views/widget/widget_gtk.h"

namespace {

const int kLanguageMainMenuSize = 5;
// TODO(glotov): need to specify the list as a part of the image customization.
const char kLanguagesTopped[] = "es,it,de,fr,en-US";
const int kMoreLanguagesSubMenu = 200;

}  // namespace

namespace chromeos {

LanguageSwitchMenu::LanguageSwitchMenu()
    : ALLOW_THIS_IN_INITIALIZER_LIST(menu_model_(this)),
      ALLOW_THIS_IN_INITIALIZER_LIST(menu_model_submenu_(this)),
      menu_alignment_(views::Menu2::ALIGN_TOPRIGHT) {
}

void LanguageSwitchMenu::InitLanguageMenu() {
  // Update LanguageList to contain entries in current locale.
  language_list_.reset(new LanguageList);
  language_list_->CopySpecifiedLanguagesUp(kLanguagesTopped);

  // Clear older menu items.
  menu_model_.Clear();
  menu_model_submenu_.Clear();

  // Fill menu items with updated items.
  for (int line = 0; line != kLanguageMainMenuSize; line++) {
    menu_model_.AddItem(line, language_list_->GetLanguageNameAt(line));
  }
  menu_model_.AddSeparator();
  menu_model_.AddSubMenuWithStringId(kMoreLanguagesSubMenu,
                                     IDS_LANGUAGES_MORE,
                                     &menu_model_submenu_);
  for (int line = kLanguageMainMenuSize;
       line != language_list_->get_languages_count(); line++) {
    menu_model_submenu_.AddItem(
        line, language_list_->GetLanguageNameAt(line));
  }

  // Initialize menu here so it appears fast when called.
  menu_.reset(new views::Menu2(&menu_model_));
}

string16 LanguageSwitchMenu::GetCurrentLocaleName() const {
  DCHECK(g_browser_process);
  const std::string locale = g_browser_process->GetApplicationLocale();
  int index = language_list_->GetIndexFromLocale(locale);
  CHECK_NE(-1, index) << "Unknown locale: " << locale;
  return language_list_->GetLanguageNameAt(index);
};

void LanguageSwitchMenu::SetFirstLevelMenuWidth(int width) {
  DCHECK(menu_ != NULL);
  menu_->SetMinimumWidth(width);
}

// static
bool LanguageSwitchMenu::SwitchLanguage(const std::string& locale) {
  DCHECK(g_browser_process);
  if (g_browser_process->GetApplicationLocale() == locale) {
    return false;
  }
  // TODO(markusheintz): Change the if condition to prefs->IsUserModifiable()
  // once Mattias landed his pending patch.
  if (!g_browser_process->local_state()->
      IsManagedPreference(prefs::kApplicationLocale)) {
    std::string loaded_locale;
    {
      // Reloading resource bundle causes us to do blocking IO on UI thread.
      // Temporarily allow it until we fix http://crosbug.com/11102
      base::ThreadRestrictions::ScopedAllowIO allow_io;
      // Switch the locale.
      loaded_locale = ResourceBundle::ReloadSharedInstance(locale);
    }
    CHECK(!loaded_locale.empty()) << "Locale could not be found for " << locale;

    LoadFontsForCurrentLocale();
    // The following line does not seem to affect locale anyhow. Maybe in
    // future..
    g_browser_process->SetApplicationLocale(locale);
    return true;
  }
  return false;
}

// static
void LanguageSwitchMenu::LoadFontsForCurrentLocale() {
  std::string gtkrc = l10n_util::GetStringUTF8(IDS_LOCALE_GTKRC);

  // Read locale-specific gtkrc.  Ideally we'd discard all the previously read
  // gtkrc information, but GTK doesn't support that.  Reading the new locale's
  // gtkrc overrides the styles from previous ones when there is a conflict, but
  // styles that are added and not conflicted will not be overridden.  So far
  // there are no locales with such a thing; if there are then this solution
  // will not work.
  if (!gtkrc.empty())
    gtk_rc_parse_string(gtkrc.c_str());
  else
    gtk_rc_parse("/etc/gtk-2.0/gtkrc");

  // Switch the font.
  gfx::PlatformFontGtk::ReloadDefaultFont();
  ResourceBundle::GetSharedInstance().ReloadFonts();
}

// static
void LanguageSwitchMenu::SwitchLanguageAndEnableKeyboardLayouts(
    const std::string& locale) {
  if (SwitchLanguage(locale)) {
    // If we have switched the locale, enable the keyboard layouts that
    // are necessary for the new locale.  Change the current input method
    // to the hardware keyboard layout since the input method currently in
    // use may not be supported by the new locale (3rd parameter).
    input_method::EnableInputMethods(
        locale, input_method::kKeyboardLayoutsOnly,
        input_method::GetHardwareInputMethodId());
  }
}

////////////////////////////////////////////////////////////////////////////////
// views::ViewMenuDelegate implementation.

void LanguageSwitchMenu::RunMenu(views::View* source, const gfx::Point& pt) {
  DCHECK(menu_ != NULL);
  views::MenuButton* button = static_cast<views::MenuButton*>(source);
  // We align the on left edge of the button for non RTL case.
  gfx::Point new_pt(pt);
  if (menu_alignment_ == views::Menu2::ALIGN_TOPLEFT) {
    int reverse_offset = button->width() + button->menu_offset().x() * 2;
    if (base::i18n::IsRTL()) {
      new_pt.set_x(pt.x() + reverse_offset);
    } else {
      new_pt.set_x(pt.x() - reverse_offset);
    }
  }
  menu_->RunMenuAt(new_pt, menu_alignment_);
}

////////////////////////////////////////////////////////////////////////////////
// ui::SimpleMenuModel::Delegate implementation.

bool LanguageSwitchMenu::IsCommandIdChecked(int command_id) const {
  return false;
}

bool LanguageSwitchMenu::IsCommandIdEnabled(int command_id) const {
  return true;
}

bool LanguageSwitchMenu::GetAcceleratorForCommandId(
    int command_id, ui::Accelerator* accelerator) {
  return false;
}

void LanguageSwitchMenu::ExecuteCommand(int command_id) {
  const std::string locale = language_list_->GetLocaleFromIndex(command_id);
  // Here, we should enable keyboard layouts associated with the locale so
  // that users can use those keyboard layouts on the login screen.
  SwitchLanguageAndEnableKeyboardLayouts(locale);
  g_browser_process->local_state()->SetString(
      prefs::kApplicationLocale, locale);
  g_browser_process->local_state()->ScheduleSavePersistentPrefs();
  InitLanguageMenu();

  // Update all view hierarchies that the locale has changed.
  views::Widget::NotifyLocaleChanged();
}

}  // namespace chromeos