summaryrefslogtreecommitdiffstats
path: root/chrome/browser/chromeos/options/language_config_model.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/chromeos/options/language_config_model.cc')
-rw-r--r--chrome/browser/chromeos/options/language_config_model.cc478
1 files changed, 478 insertions, 0 deletions
diff --git a/chrome/browser/chromeos/options/language_config_model.cc b/chrome/browser/chromeos/options/language_config_model.cc
new file mode 100644
index 0000000..e1af5a47
--- /dev/null
+++ b/chrome/browser/chromeos/options/language_config_model.cc
@@ -0,0 +1,478 @@
+// 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/chromeos/options/language_config_model.h"
+
+#include <algorithm>
+#include <functional>
+#include <utility>
+
+#include "app/l10n_util_collator.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/cros/language_library.h"
+#include "chrome/browser/chromeos/preferences.h"
+#include "chrome/browser/chromeos/status/language_menu_l10n_util.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/pref_names.h"
+
+namespace chromeos {
+
+namespace {
+
+// The code should be compatible with one of codes used for UI languages,
+// defined in app/l10_util.cc.
+const char kDefaultLanguageCode[] = "en-US";
+
+// The list of language that do not have associated input methods. For
+// these languages, we associate input methods here.
+const struct ExtraLanguage {
+ const char* language_code;
+ const char* input_method_id;
+} kExtraLanguages[] = {
+ { "id", "xkb:us::eng" }, // For Indonesian, use US keyboard layout.
+ // The code "fil" comes from app/l10_util.cc.
+ { "fil", "xkb:us::eng" }, // For Filipino, use US keyboard layout.
+ // The code "es-419" comes from app/l10_util.cc.
+ // For Spanish in Latin America, use Spanish keyboard layout.
+ { "es-419", "xkb:es::spa" },
+};
+
+// The list defines pairs of language code and the default input method
+// id. The list is used for reordering input method ids.
+//
+// TODO(satorux): We may need to handle secondary, and ternary input
+// methods, rather than handling the default input method only.
+const struct LanguageDefaultInputMethodId {
+ const char* language_code;
+ const char* input_method_id;
+} kLanguageDefaultInputMethodIds[] = {
+ { "en-US", "xkb:us::eng", }, // US - English
+ { "fr", "xkb:fr::fra", }, // France - French
+ { "de", "xkb:de::ger", }, // Germany - German
+};
+
+} // namespace
+
+AddLanguageComboboxModel::AddLanguageComboboxModel(
+ Profile* profile,
+ const std::vector<std::string>& locale_codes)
+ : LanguageComboboxModel(profile, locale_codes) {
+}
+
+int AddLanguageComboboxModel::GetItemCount() {
+ // +1 for "Add language".
+ return get_languages_count() + 1 - ignore_set_.size();
+}
+
+std::wstring AddLanguageComboboxModel::GetItemAt(int index) {
+ // Show "Add language" as the first item.
+ if (index == 0) {
+ return l10n_util::GetString(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_ADD_LANGUAGE_COMBOBOX);
+ }
+ return LanguageConfigModel::MaybeRewriteLanguageName(
+ GetLanguageNameAt(GetLanguageIndex(index)));
+}
+
+int AddLanguageComboboxModel::GetLanguageIndex(int index) const {
+ // The adjusted_index is counted while ignoring languages in ignore_set_.
+ int adjusted_index = 0;
+ for (int i = 0; i < get_languages_count(); ++i) {
+ if (ignore_set_.count(GetLocaleFromIndex(i)) > 0) {
+ continue;
+ }
+ // -1 for "Add language".
+ if (adjusted_index == index - 1) {
+ return i;
+ }
+ ++adjusted_index;
+ }
+ return 0;
+}
+
+void AddLanguageComboboxModel::SetIgnored(
+ const std::string& language_code, bool ignored) {
+ if (ignored) {
+ // Add to the ignore_set_ if the language code is known (i.e. reject
+ // unknown language codes just in case).
+ if (GetIndexFromLocale(language_code) != -1) {
+ ignore_set_.insert(language_code);
+ } else {
+ LOG(ERROR) << "Unknown language code: " << language_code;
+ }
+ } else {
+ ignore_set_.erase(language_code);
+ }
+}
+
+LanguageConfigModel::LanguageConfigModel(PrefService* pref_service)
+ : pref_service_(pref_service) {
+}
+
+void LanguageConfigModel::Init() {
+ // Initialize the maps and vectors.
+ InitInputMethodIdMapsAndVectors();
+
+ preload_engines_.Init(
+ prefs::kLanguagePreloadEngines, pref_service_, this);
+ // TODO(yusukes): It might be safer to call GetActiveLanguages() cros API
+ // here and compare the result and preload_engines_.GetValue(). If there's
+ // a discrepancy between IBus setting and Chrome prefs, we can resolve it
+ // by calling preload_engines_SetValue() here.
+}
+
+size_t LanguageConfigModel::CountNumActiveInputMethods(
+ const std::string& language_code) {
+ int num_selected_active_input_methods = 0;
+ std::pair<LanguageCodeToIdsMap::const_iterator,
+ LanguageCodeToIdsMap::const_iterator> range =
+ language_code_to_ids_map_.equal_range(language_code);
+ for (LanguageCodeToIdsMap::const_iterator iter = range.first;
+ iter != range.second; ++iter) {
+ if (InputMethodIsActivated(iter->second)) {
+ ++num_selected_active_input_methods;
+ }
+ }
+ return num_selected_active_input_methods;
+}
+
+bool LanguageConfigModel::HasLanguageCode(
+ const std::string& language_code) const {
+ return std::find(preferred_language_codes_.begin(),
+ preferred_language_codes_.end(),
+ language_code) != preferred_language_codes_.end();
+}
+
+size_t LanguageConfigModel::AddLanguageCode(
+ const std::string& language_code) {
+ preferred_language_codes_.push_back(language_code);
+ // Sort the language codes by names. This is not efficient, but
+ // acceptable as the language list is about 40 item long at most. In
+ // theory, we could find the position to insert rather than sorting, but
+ // it would be complex as we need to use unicode string comparator.
+ SortLanguageCodesByNames(&preferred_language_codes_);
+ // Find the language code just added in the sorted language codes.
+ const int added_at =
+ std::distance(preferred_language_codes_.begin(),
+ std::find(preferred_language_codes_.begin(),
+ preferred_language_codes_.end(),
+ language_code));
+ return added_at;
+}
+
+void LanguageConfigModel::RemoveLanguageAt(size_t row) {
+ preferred_language_codes_.erase(preferred_language_codes_.begin() + row);
+}
+
+void LanguageConfigModel::UpdateInputMethodPreferences(
+ const std::vector<std::string>& in_new_input_method_ids) {
+ std::vector<std::string> new_input_method_ids = in_new_input_method_ids;
+ // Note: Since |new_input_method_ids| is alphabetically sorted and the sort
+ // function below uses stable sort, the relateve order of input methods that
+ // belong to the same language (e.g. "mozc" and "xkb:jp::jpn") is maintained.
+ SortInputMethodIdsByNames(id_to_language_code_map_, &new_input_method_ids);
+ preload_engines_.SetValue(UTF8ToWide(JoinString(new_input_method_ids, ',')));
+}
+
+void LanguageConfigModel::DeactivateInputMethodsFor(
+ const std::string& language_code) {
+ for (size_t i = 0; i < num_supported_input_method_ids(); ++i) {
+ if (GetLanguageCodeFromInputMethodId(
+ supported_input_method_id_at(i)) ==
+ language_code) {
+ // What happens if we disable the input method currently active?
+ // IBus should take care of it, so we don't do anything special
+ // here. See crosbug.com/2443.
+ SetInputMethodActivated(supported_input_method_id_at(i), false);
+ // Do not break; here in order to disable all engines that belong to
+ // |language_code|.
+ }
+ }
+}
+
+void LanguageConfigModel::SetInputMethodActivated(
+ const std::string& input_method_id, bool activated) {
+ DCHECK(!input_method_id.empty());
+ std::vector<std::string> input_method_ids;
+ GetActiveInputMethodIds(&input_method_ids);
+
+ std::set<std::string> input_method_id_set(input_method_ids.begin(),
+ input_method_ids.end());
+ if (activated) {
+ // Add |id| if it's not already added.
+ input_method_id_set.insert(input_method_id);
+ } else {
+ input_method_id_set.erase(input_method_id);
+ }
+
+ // Update Chrome's preference.
+ std::vector<std::string> new_input_method_ids(input_method_id_set.begin(),
+ input_method_id_set.end());
+ UpdateInputMethodPreferences(new_input_method_ids);
+}
+
+bool LanguageConfigModel::InputMethodIsActivated(
+ const std::string& input_method_id) {
+ std::vector<std::string> input_method_ids;
+ GetActiveInputMethodIds(&input_method_ids);
+ return (std::find(input_method_ids.begin(), input_method_ids.end(),
+ input_method_id) != input_method_ids.end());
+}
+
+void LanguageConfigModel::GetActiveInputMethodIds(
+ std::vector<std::string>* out_input_method_ids) {
+ const std::wstring value = preload_engines_.GetValue();
+ out_input_method_ids->clear();
+ if (!value.empty()) {
+ SplitString(WideToUTF8(value), ',', out_input_method_ids);
+ }
+}
+
+std::string LanguageConfigModel::GetLanguageCodeFromInputMethodId(
+ const std::string& input_method_id) const {
+ std::map<std::string, std::string>::const_iterator iter
+ = id_to_language_code_map_.find(input_method_id);
+ return (iter == id_to_language_code_map_.end()) ?
+ // Returning |kDefaultLanguageCode| is not for Chrome OS but for Ubuntu
+ // where the ibus-xkb-layouts module could be missing.
+ kDefaultLanguageCode : iter->second;
+}
+
+std::string LanguageConfigModel::GetInputMethodDisplayNameFromId(
+ const std::string& input_method_id) const {
+ // |kDefaultDisplayName| is not for Chrome OS. See the comment above.
+ static const char kDefaultDisplayName[] = "USA";
+ std::map<std::string, std::string>::const_iterator iter
+ = id_to_display_name_map_.find(input_method_id);
+ return (iter == id_to_display_name_map_.end()) ?
+ kDefaultDisplayName : iter->second;
+}
+
+void LanguageConfigModel::GetInputMethodIdsFromLanguageCode(
+ const std::string& language_code,
+ std::vector<std::string>* input_method_ids) const {
+ DCHECK(input_method_ids);
+ input_method_ids->clear();
+
+ std::pair<LanguageCodeToIdsMap::const_iterator,
+ LanguageCodeToIdsMap::const_iterator> range =
+ language_code_to_ids_map_.equal_range(language_code);
+ for (LanguageCodeToIdsMap::const_iterator iter = range.first;
+ iter != range.second; ++iter) {
+ input_method_ids->push_back(iter->second);
+ }
+ // Reorder the input methods.
+ ReorderInputMethodIdsForLanguageCode(language_code, input_method_ids);
+}
+
+void LanguageConfigModel::NotifyPrefChanged() {
+ std::vector<std::string> input_method_ids;
+ GetActiveInputMethodIds(&input_method_ids);
+
+ std::set<std::string> language_code_set;
+ for (size_t i = 0; i < input_method_ids.size(); ++i) {
+ const std::string language_code =
+ GetLanguageCodeFromInputMethodId(input_method_ids[i]);
+ language_code_set.insert(language_code);
+ }
+
+ preferred_language_codes_.clear();
+ preferred_language_codes_.assign(
+ language_code_set.begin(), language_code_set.end());
+ LanguageConfigModel::SortLanguageCodesByNames(&preferred_language_codes_);
+}
+
+void LanguageConfigModel::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::PREF_CHANGED) {
+ NotifyPrefChanged();
+ }
+}
+
+std::wstring LanguageConfigModel::MaybeRewriteLanguageName(
+ const std::wstring& language_name) {
+ // "t" is used as the language code for input methods that don't fall
+ // under any other languages.
+ if (language_name == L"t") {
+ return l10n_util::GetString(
+ IDS_OPTIONS_SETTINGS_LANGUAGES_OTHERS);
+ }
+ return language_name;
+}
+
+std::wstring LanguageConfigModel::GetLanguageDisplayNameFromCode(
+ const std::string& language_code) {
+ return MaybeRewriteLanguageName(UTF16ToWide(
+ l10n_util::GetDisplayNameForLocale(
+ language_code, g_browser_process->GetApplicationLocale(),
+ true)));
+}
+
+namespace {
+
+// The comparator is used for sorting language codes by their
+// corresponding language names, using the ICU collator.
+struct CompareLanguageCodesByLanguageName
+ : std::binary_function<const std::string&, const std::string&, bool> {
+ explicit CompareLanguageCodesByLanguageName(icu::Collator* collator)
+ : collator_(collator) {
+ }
+
+ // Calling GetLanguageDisplayNameFromCode() in the comparator is not
+ // efficient, but acceptable as the function is cheap, and the language
+ // list is short (about 40 at most).
+ bool operator()(const std::string& s1, const std::string& s2) const {
+ const std::wstring key1 =
+ LanguageConfigModel::GetLanguageDisplayNameFromCode(s1);
+ const std::wstring key2 =
+ LanguageConfigModel::GetLanguageDisplayNameFromCode(s2);
+ return l10n_util::StringComparator<std::wstring>(collator_)(key1, key2);
+ }
+
+ icu::Collator* collator_;
+};
+
+// The comparator is used for sorting input method ids by their
+// corresponding language names, using the ICU collator.
+struct CompareInputMethodIdsByLanguageName
+ : std::binary_function<const std::string&, const std::string&, bool> {
+ CompareInputMethodIdsByLanguageName(
+ icu::Collator* collator,
+ const std::map<std::string, std::string>& id_to_language_code_map)
+ : comparator_(collator),
+ id_to_language_code_map_(id_to_language_code_map) {
+ }
+
+ bool operator()(const std::string& s1, const std::string& s2) const {
+ std::string language_code_1;
+ std::map<std::string, std::string>::const_iterator iter =
+ id_to_language_code_map_.find(s1);
+ if (iter != id_to_language_code_map_.end()) {
+ language_code_1 = iter->second;
+ }
+ std::string language_code_2;
+ iter = id_to_language_code_map_.find(s2);
+ if (iter != id_to_language_code_map_.end()) {
+ language_code_2 = iter->second;
+ }
+ return comparator_(language_code_1, language_code_2);
+ }
+
+ const CompareLanguageCodesByLanguageName comparator_;
+ const std::map<std::string, std::string>& id_to_language_code_map_;
+};
+
+} // namespace
+
+void LanguageConfigModel::SortLanguageCodesByNames(
+ std::vector<std::string>* language_codes) {
+ // We should build collator outside of the comparator. We cannot have
+ // scoped_ptr<> in the comparator for a subtle STL reason.
+ UErrorCode error = U_ZERO_ERROR;
+ icu::Locale locale(g_browser_process->GetApplicationLocale().c_str());
+ scoped_ptr<icu::Collator> collator(
+ icu::Collator::createInstance(locale, error));
+ if (U_FAILURE(error)) {
+ collator.reset();
+ }
+ std::sort(language_codes->begin(), language_codes->end(),
+ CompareLanguageCodesByLanguageName(collator.get()));
+}
+
+void LanguageConfigModel::SortInputMethodIdsByNames(
+ const std::map<std::string, std::string>& id_to_language_code_map,
+ std::vector<std::string>* input_method_ids) {
+ UErrorCode error = U_ZERO_ERROR;
+ icu::Locale locale(g_browser_process->GetApplicationLocale().c_str());
+ scoped_ptr<icu::Collator> collator(
+ icu::Collator::createInstance(locale, error));
+ if (U_FAILURE(error)) {
+ collator.reset();
+ }
+ std::stable_sort(input_method_ids->begin(), input_method_ids->end(),
+ CompareInputMethodIdsByLanguageName(
+ collator.get(), id_to_language_code_map));
+}
+
+void LanguageConfigModel::ReorderInputMethodIdsForLanguageCode(
+ const std::string& language_code,
+ std::vector<std::string>* input_method_ids) {
+ for (size_t i = 0; i < arraysize(kLanguageDefaultInputMethodIds); ++i) {
+ if (language_code == kLanguageDefaultInputMethodIds[i].language_code) {
+ std::vector<std::string>::iterator iter =
+ std::find(input_method_ids->begin(), input_method_ids->end(),
+ kLanguageDefaultInputMethodIds[i].input_method_id);
+ // If it's not on the top of |input_method_id|, swap it with the top one.
+ if (iter != input_method_ids->end() &&
+ iter != input_method_ids->begin()) {
+ std::swap(*input_method_ids->begin(), *iter);
+ }
+ break; // Don't have to check other language codes.
+ }
+ }
+}
+
+void LanguageConfigModel::InitInputMethodIdMapsAndVectors() {
+ // The two sets are used to build lists without duplication.
+ std::set<std::string> supported_language_code_set;
+ std::set<std::string> supported_input_method_id_set;
+ // Build the id to descriptor map for handling kExtraLanguages later.
+ std::map<std::string, const InputMethodDescriptor*> id_to_descriptor_map;
+
+ // GetSupportedLanguages() never return NULL.
+ scoped_ptr<InputMethodDescriptors> supported_input_methods(
+ CrosLibrary::Get()->GetLanguageLibrary()->GetSupportedInputMethods());
+ for (size_t i = 0; i < supported_input_methods->size(); ++i) {
+ const InputMethodDescriptor& input_method = supported_input_methods->at(i);
+ const std::string language_code =
+ LanguageLibrary::GetLanguageCodeFromDescriptor(input_method);
+ AddInputMethodToMaps(language_code, input_method);
+ // Add the language code and the input method id to the sets.
+ supported_language_code_set.insert(language_code);
+ supported_input_method_id_set.insert(input_method.id);
+ // Remember the pair.
+ id_to_descriptor_map.insert(
+ std::make_pair(input_method.id, &input_method));
+ }
+
+ // Go through the languages listed in kExtraLanguages.
+ for (size_t i = 0; i < arraysize(kExtraLanguages); ++i) {
+ const char* language_code = kExtraLanguages[i].language_code;
+ const char* input_method_id = kExtraLanguages[i].input_method_id;
+ std::map<std::string, const InputMethodDescriptor*>::const_iterator iter =
+ id_to_descriptor_map.find(input_method_id);
+ // If the associated input method descriptor is found, add the
+ // language code and the input method.
+ if (iter != id_to_descriptor_map.end()) {
+ const InputMethodDescriptor& input_method = *(iter->second);
+ AddInputMethodToMaps(language_code, input_method);
+ // Add the language code and the input method id to the sets.
+ supported_language_code_set.insert(language_code);
+ supported_input_method_id_set.insert(input_method.id);
+ }
+ }
+
+ // Build the vectors from the sets.
+ supported_language_codes_.assign(supported_language_code_set.begin(),
+ supported_language_code_set.end());
+ supported_input_method_ids_.assign(supported_input_method_id_set.begin(),
+ supported_input_method_id_set.end());
+}
+
+void LanguageConfigModel::AddInputMethodToMaps(
+ const std::string& language_code,
+ const InputMethodDescriptor& input_method) {
+ id_to_language_code_map_.insert(
+ std::make_pair(input_method.id, language_code));
+ id_to_display_name_map_.insert(
+ std::make_pair(input_method.id, LanguageMenuL10nUtil::GetStringUTF8(
+ input_method.display_name)));
+ language_code_to_ids_map_.insert(
+ std::make_pair(language_code, input_method.id));
+}
+
+} // namespace chromeos