// 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/input_method/input_method_util.h" #include #include #include #include #include "unicode/uloc.h" #include "base/basictypes.h" #include "base/hash_tables.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/string_split.h" #include "base/string_util.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/language_preferences.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/common/pref_names.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util_collator.h" namespace { // Map from language code to associated input method IDs, etc. typedef std::multimap LanguageCodeToIdsMap; // Map from input method ID to associated input method descriptor. typedef std::map InputMethodIdToDescriptorMap; // Map from layout name to associated overlay ID typedef std::map InputMethodNameToOverlayIdMap; struct IdMaps { scoped_ptr language_code_to_ids; scoped_ptr > id_to_language_code; scoped_ptr id_to_descriptor; scoped_ptr > name_to_overlay_id; // Returns the singleton instance. static IdMaps* GetInstance() { return Singleton::get(); } void ReloadMaps() { chromeos::InputMethodLibrary* library = chromeos::CrosLibrary::Get()->GetInputMethodLibrary(); scoped_ptr supported_input_methods( library->GetSupportedInputMethods()); if (supported_input_methods->size() <= 1) { LOG(ERROR) << "GetSupportedInputMethods returned a fallback ID"; // TODO(yusukes): Handle this error in nicer way. } // Clear the existing maps. language_code_to_ids->clear(); id_to_language_code->clear(); id_to_descriptor->clear(); name_to_overlay_id->clear(); for (size_t i = 0; i < supported_input_methods->size(); ++i) { const chromeos::InputMethodDescriptor& input_method = supported_input_methods->at(i); const std::string language_code = chromeos::input_method::GetLanguageCodeFromDescriptor(input_method); const std::string keyboard_overlay_id = library->GetKeyboardOverlayId(input_method.id); language_code_to_ids->insert( std::make_pair(language_code, input_method.id)); // Remember the pairs. id_to_language_code->insert( std::make_pair(input_method.id, language_code)); id_to_descriptor->insert( std::make_pair(input_method.id, input_method)); name_to_overlay_id->insert( std::make_pair(input_method.keyboard_layout, keyboard_overlay_id)); } // Go through the languages listed in kExtraLanguages. using chromeos::input_method::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; InputMethodIdToDescriptorMap::const_iterator iter = id_to_descriptor->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->end()) { const chromeos::InputMethodDescriptor& input_method = iter->second; const std::string keyboard_overlay_id = library->GetKeyboardOverlayId(input_method.id); language_code_to_ids->insert( std::make_pair(language_code, input_method.id)); name_to_overlay_id->insert( std::make_pair(input_method.keyboard_layout, keyboard_overlay_id)); } } } private: IdMaps() : language_code_to_ids(new LanguageCodeToIdsMap), id_to_language_code(new std::map), id_to_descriptor(new InputMethodIdToDescriptorMap), name_to_overlay_id(new std::map) { ReloadMaps(); } friend struct DefaultSingletonTraits; DISALLOW_COPY_AND_ASSIGN(IdMaps); }; const struct EnglishToResouceId { const char* english_string_from_ibus; int resource_id; } kEnglishToResourceIdArray[] = { // For ibus-mozc. { "Direct input", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_DIRECT_INPUT }, { "Hiragana", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_HIRAGANA }, { "Katakana", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_KATAKANA }, { "Half width katakana", // small k is not a typo. IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_HALF_WIDTH_KATAKANA }, { "Latin", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_LATIN }, { "Wide Latin", IDS_STATUSBAR_IME_JAPANESE_IME_STATUS_WIDE_LATIN }, // For ibus-hangul: third_party/ibus-hangul/files/po/. { "Enable/Disable Hanja mode", IDS_STATUSBAR_IME_KOREAN_HANJA_MODE }, // For ibus-pinyin. { "Full/Half width", IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF }, { "Full/Half width punctuation", IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF_PUNCTUATION }, { "Simplfied/Traditional Chinese", IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_S_T_CHINESE }, // For ibus-mozc-chewing. { "English", IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_ENGLISH_MODE }, { "Full-width English", IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_FULL_WIDTH_ENGLISH_MODE }, // For the "Languages and Input" dialog. { "kbd (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD }, { "itrans (m17n)", // also uses the "STANDARD_INPUT_METHOD" id. IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD }, { "cangjie (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_CANGJIE_INPUT_METHOD }, { "quick (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_QUICK_INPUT_METHOD }, { "isiri (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_PERSIAN_ISIRI_2901_INPUT_METHOD }, { "kesmanee (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_KESMANEE_INPUT_METHOD }, { "tis820 (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_TIS820_INPUT_METHOD }, { "pattachote (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_PATTACHOTE_INPUT_METHOD }, { "tcvn (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TCVN_INPUT_METHOD }, { "telex (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TELEX_INPUT_METHOD }, { "viqr (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VIQR_INPUT_METHOD }, { "vni (m17n)", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VNI_INPUT_METHOD }, { "Mozc Chewing (Chewing)", IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_INPUT_METHOD }, { "Pinyin", IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_INPUT_METHOD }, { "Mozc (US keyboard layout)", IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_US_INPUT_METHOD }, { "Mozc (US Dvorak keyboard layout)", IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_US_DV_INPUT_METHOD }, { "Mozc (Japanese keyboard layout)", IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_MOZC_JP_INPUT_METHOD }, { "Google Japanese Input (US keyboard layout)", IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_US_INPUT_METHOD }, { "Google Japanese Input (US Dvorak keyboard layout)", IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_US_DV_INPUT_METHOD }, { "Google Japanese Input (Japanese keyboard layout)", IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_GOOGLE_JP_INPUT_METHOD }, { "Korean", IDS_OPTIONS_SETTINGS_LANGUAGES_KOREAN_INPUT_METHOD }, // For ibus-xkb-layouts engine: third_party/ibus-xkb-layouts/files { "Japan", IDS_STATUSBAR_LAYOUT_JAPAN }, { "Slovenia", IDS_STATUSBAR_LAYOUT_SLOVENIA }, { "Germany", IDS_STATUSBAR_LAYOUT_GERMANY }, { "Germany - Neo 2", IDS_STATUSBAR_LAYOUT_GERMANY_NEO2 }, { "Italy", IDS_STATUSBAR_LAYOUT_ITALY }, { "Estonia", IDS_STATUSBAR_LAYOUT_ESTONIA }, { "Hungary", IDS_STATUSBAR_LAYOUT_HUNGARY }, { "Poland", IDS_STATUSBAR_LAYOUT_POLAND }, { "Denmark", IDS_STATUSBAR_LAYOUT_DENMARK }, { "Croatia", IDS_STATUSBAR_LAYOUT_CROATIA }, { "Brazil", IDS_STATUSBAR_LAYOUT_BRAZIL }, { "Serbia", IDS_STATUSBAR_LAYOUT_SERBIA }, { "Czechia", IDS_STATUSBAR_LAYOUT_CZECHIA }, { "USA - Dvorak", IDS_STATUSBAR_LAYOUT_USA_DVORAK }, { "USA - Colemak", IDS_STATUSBAR_LAYOUT_USA_COLEMAK }, { "Romania", IDS_STATUSBAR_LAYOUT_ROMANIA }, { "USA", IDS_STATUSBAR_LAYOUT_USA }, { "USA - International (AltGr dead keys)", IDS_STATUSBAR_LAYOUT_USA_EXTENDED }, { "USA - International (with dead keys)", IDS_STATUSBAR_LAYOUT_USA_INTERNATIONAL }, { "Lithuania", IDS_STATUSBAR_LAYOUT_LITHUANIA }, { "United Kingdom - Extended - Winkeys", IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM }, { "United Kingdom - Dvorak", IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM_DVORAK }, { "Slovakia", IDS_STATUSBAR_LAYOUT_SLOVAKIA }, { "Russia", IDS_STATUSBAR_LAYOUT_RUSSIA }, { "Russia - Phonetic", IDS_STATUSBAR_LAYOUT_RUSSIA_PHONETIC }, { "Greece", IDS_STATUSBAR_LAYOUT_GREECE }, { "Belgium", IDS_STATUSBAR_LAYOUT_BELGIUM }, { "Bulgaria", IDS_STATUSBAR_LAYOUT_BULGARIA }, { "Bulgaria - Traditional phonetic", IDS_STATUSBAR_LAYOUT_BULGARIA_PHONETIC }, { "Switzerland", IDS_STATUSBAR_LAYOUT_SWITZERLAND }, { "Switzerland - French", IDS_STATUSBAR_LAYOUT_SWITZERLAND_FRENCH }, { "Turkey", IDS_STATUSBAR_LAYOUT_TURKEY }, { "Portugal", IDS_STATUSBAR_LAYOUT_PORTUGAL }, { "Spain", IDS_STATUSBAR_LAYOUT_SPAIN }, { "Finland", IDS_STATUSBAR_LAYOUT_FINLAND }, { "Ukraine", IDS_STATUSBAR_LAYOUT_UKRAINE }, { "Spain - Catalan variant with middle-dot L", IDS_STATUSBAR_LAYOUT_SPAIN_CATALAN }, { "France", IDS_STATUSBAR_LAYOUT_FRANCE }, { "Norway", IDS_STATUSBAR_LAYOUT_NORWAY }, { "Sweden", IDS_STATUSBAR_LAYOUT_SWEDEN }, { "Netherlands", IDS_STATUSBAR_LAYOUT_NETHERLANDS }, { "Latin American", IDS_STATUSBAR_LAYOUT_LATIN_AMERICAN }, { "Latvia - Apostrophe (') variant", IDS_STATUSBAR_LAYOUT_LATVIA }, { "Canada", IDS_STATUSBAR_LAYOUT_CANADA }, { "Canada - English", IDS_STATUSBAR_LAYOUT_CANADA_ENGLISH }, { "Israel", IDS_STATUSBAR_LAYOUT_ISRAEL }, { "Korea, Republic of - 101/104 key Compatible", IDS_STATUSBAR_LAYOUT_KOREA_104 }, }; const size_t kEnglishToResourceIdArraySize = arraysize(kEnglishToResourceIdArray); const struct EnglishAndInputMethodIdToResouceId { const char* english_string_from_ibus; const char* input_method_id; int resource_id; } kEnglishAndInputMethodIdToResourceIdArray[] = { { "Chinese", "pinyin", IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_CHINESE_ENGLISH }, { "Chinese", "mozc-chewing", IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_CHINESE_MODE }, }; const size_t kEnglishAndInputMethodIdToResourceIdArraySize = arraysize(kEnglishAndInputMethodIdToResourceIdArray); // There are some differences between ISO 639-2 (T) and ISO 639-2 B, and // some language codes are not recognized by ICU (i.e. ICU cannot convert // these codes to two-letter language codes and display names). Hence we // convert these codes to ones that ICU recognize. // // See http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes for details. const char* kIso639VariantMapping[][2] = { { "cze", "ces" }, { "ger", "deu" }, { "gre", "ell" }, // "scr" is not a ISO 639 code. For some reason, evdev.xml uses "scr" as // the language code for Croatian. { "scr", "hrv" }, { "rum", "ron" }, { "slo", "slk" }, }; // The comparator is used for sorting language codes by their // corresponding language names, using the ICU collator. struct CompareLanguageCodesByLanguageName : std::binary_function { 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 string16 key1 = chromeos::input_method::GetLanguageDisplayNameFromCode(s1); const string16 key2 = chromeos::input_method::GetLanguageDisplayNameFromCode(s2); return l10n_util::StringComparator(collator_)(key1, key2); } private: icu::Collator* collator_; }; bool GetLocalizedString(const std::string& english_string, const std::string& input_method_id, string16 *out_string) { DCHECK(out_string); // Initialize the primary map if needed. typedef base::hash_map HashType; static HashType* english_to_resource_id = NULL; if (!english_to_resource_id) { // We don't free this map. english_to_resource_id = new HashType(kEnglishToResourceIdArraySize); for (size_t i = 0; i < kEnglishToResourceIdArraySize; ++i) { const EnglishToResouceId& map_entry = kEnglishToResourceIdArray[i]; const bool result = english_to_resource_id->insert(std::make_pair( map_entry.english_string_from_ibus, map_entry.resource_id)).second; DCHECK(result) << "Duplicated string is found: " << map_entry.english_string_from_ibus; } } // Initialize the secondary map if needed. typedef std::map, int> MapType; static MapType* english_and_input_method_id_to_resource_id = NULL; if (!english_and_input_method_id_to_resource_id) { // We don't free this map. english_and_input_method_id_to_resource_id = new MapType; for (size_t i = 0; i < kEnglishAndInputMethodIdToResourceIdArraySize; ++i) { const EnglishAndInputMethodIdToResouceId& map_entry = kEnglishAndInputMethodIdToResourceIdArray[i]; const std::pair key = std::make_pair( map_entry.english_string_from_ibus, map_entry.input_method_id); const bool result = english_and_input_method_id_to_resource_id->insert( std::make_pair(key, map_entry.resource_id)).second; DCHECK(result) << "Duplicated key is found: pair of " << map_entry.english_string_from_ibus << " and " << map_entry.input_method_id; } } HashType::const_iterator iter = english_to_resource_id->find(english_string); if (iter == english_to_resource_id->end()) { // The string is not found in the primary map. Try the secondary map with // |input_method_id|. const std::pair key = std::make_pair(english_string, input_method_id); MapType::const_iterator iter2 = english_and_input_method_id_to_resource_id->find(key); if (iter2 == english_and_input_method_id_to_resource_id->end()) { // TODO(yusukes): Write Autotest which checks if all display names and all // property names for supported input methods are listed in the resource // ID array (crosbug.com/4572). LOG(ERROR) << "Resource ID is not found for: " << english_string; return false; } *out_string = l10n_util::GetStringUTF16(iter2->second); } else { *out_string = l10n_util::GetStringUTF16(iter->second); } return true; }; } // namespace namespace chromeos { namespace input_method { std::wstring GetString(const std::string& english_string, const std::string& input_method_id) { string16 localized_string; if (GetLocalizedString(english_string, input_method_id, &localized_string)) { return UTF16ToWide(localized_string); } return UTF8ToWide(english_string); } std::string GetStringUTF8(const std::string& english_string, const std::string& input_method_id) { string16 localized_string; if (GetLocalizedString(english_string, input_method_id, &localized_string)) { return UTF16ToUTF8(localized_string); } return english_string; } string16 GetStringUTF16(const std::string& english_string, const std::string& input_method_id) { string16 localized_string; if (GetLocalizedString(english_string, input_method_id, &localized_string)) { return localized_string; } return UTF8ToUTF16(english_string); } bool StringIsSupported(const std::string& english_string, const std::string& input_method_id) { string16 localized_string; return GetLocalizedString(english_string, input_method_id, &localized_string); } std::string NormalizeLanguageCode( const std::string& language_code) { // Some ibus engines return locale codes like "zh_CN" as language codes. // Normalize these to like "zh-CN". if (language_code.size() >= 5 && language_code[2] == '_') { std::string copied_language_code = language_code; copied_language_code[2] = '-'; // Downcase the language code part. for (size_t i = 0; i < 2; ++i) { copied_language_code[i] = base::ToLowerASCII(copied_language_code[i]); } // Upcase the country code part. for (size_t i = 3; i < copied_language_code.size(); ++i) { copied_language_code[i] = base::ToUpperASCII(copied_language_code[i]); } return copied_language_code; } // We only handle three-letter codes from here. if (language_code.size() != 3) { return language_code; } // Convert special language codes. See comments at kIso639VariantMapping. std::string copied_language_code = language_code; for (size_t i = 0; i < arraysize(kIso639VariantMapping); ++i) { if (language_code == kIso639VariantMapping[i][0]) { copied_language_code = kIso639VariantMapping[i][1]; } } // Convert the three-letter code to two letter-code. UErrorCode error = U_ZERO_ERROR; char two_letter_code[ULOC_LANG_CAPACITY]; uloc_getLanguage(copied_language_code.c_str(), two_letter_code, sizeof(two_letter_code), &error); if (U_FAILURE(error)) { return language_code; } return two_letter_code; } bool IsKeyboardLayout(const std::string& input_method_id) { const bool kCaseInsensitive = false; return StartsWithASCII(input_method_id, "xkb:", kCaseInsensitive); } std::string GetLanguageCodeFromDescriptor( const InputMethodDescriptor& descriptor) { // Handle some Chinese input methods as zh-CN/zh-TW, rather than zh. // TODO: we should fix this issue in engines rather than here. if (descriptor.language_code == "zh") { if (descriptor.id == "pinyin") { return "zh-CN"; } else if (descriptor.id == "mozc-chewing" || descriptor.id == "m17n:zh:cangjie" || descriptor.id == "m17n:zh:quick") { return "zh-TW"; } } std::string language_code = NormalizeLanguageCode(descriptor.language_code); // Add country codes to language codes of some XKB input methods to make // these compatible with Chrome's application locale codes like "en-US". // TODO(satorux): Maybe we need to handle "es" for "es-419". // TODO: We should not rely on the format of the engine name. Should we add // |country_code| in InputMethodDescriptor? if (IsKeyboardLayout(descriptor.id) && (language_code == "en" || language_code == "zh" || language_code == "pt")) { std::vector portions; base::SplitString(descriptor.id, ':', &portions); if (portions.size() >= 2 && !portions[1].empty()) { language_code.append("-"); language_code.append(StringToUpperASCII(portions[1])); } } return language_code; } std::string GetLanguageCodeFromInputMethodId( const std::string& input_method_id) { // The code should be compatible with one of codes used for UI languages, // defined in app/l10_util.cc. const char kDefaultLanguageCode[] = "en-US"; std::map::const_iterator iter = IdMaps::GetInstance()->id_to_language_code->find(input_method_id); return (iter == IdMaps::GetInstance()->id_to_language_code->end()) ? // Returning |kDefaultLanguageCode| here is not for Chrome OS but for // Ubuntu where the ibus-xkb-layouts engine could be missing. kDefaultLanguageCode : iter->second; } std::string GetKeyboardLayoutName(const std::string& input_method_id) { InputMethodIdToDescriptorMap::const_iterator iter = IdMaps::GetInstance()->id_to_descriptor->find(input_method_id); return (iter == IdMaps::GetInstance()->id_to_descriptor->end()) ? "" : iter->second.keyboard_layout; } std::string GetKeyboardOverlayId(const std::string& input_method_name) { std::map::const_iterator iter = IdMaps::GetInstance()->name_to_overlay_id->find(input_method_name); return (iter == IdMaps::GetInstance()->name_to_overlay_id->end()) ? "" : iter->second; } std::string GetInputMethodDisplayNameFromId( const std::string& input_method_id) { InputMethodIdToDescriptorMap::const_iterator iter = IdMaps::GetInstance()->id_to_descriptor->find(input_method_id); return (iter == IdMaps::GetInstance()->id_to_descriptor->end()) ? "" : GetStringUTF8(iter->second.display_name, input_method_id); } const chromeos::InputMethodDescriptor* GetInputMethodDescriptorFromId( const std::string& input_method_id) { InputMethodIdToDescriptorMap::const_iterator iter = IdMaps::GetInstance()->id_to_descriptor->find(input_method_id); return (iter == IdMaps::GetInstance()->id_to_descriptor->end()) ? NULL : &(iter->second); } string16 GetLanguageDisplayNameFromCode(const std::string& language_code) { if (!g_browser_process) { return string16(); } return l10n_util::GetDisplayNameForLocale( language_code, g_browser_process->GetApplicationLocale(), true); } string16 GetLanguageNativeDisplayNameFromCode( const std::string& language_code) { return l10n_util::GetDisplayNameForLocale(language_code, language_code, true); } void SortLanguageCodesByNames(std::vector* language_codes) { if (!g_browser_process) { return; } // 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 collator( icu::Collator::createInstance(locale, error)); if (U_FAILURE(error)) { collator.reset(); } std::sort(language_codes->begin(), language_codes->end(), CompareLanguageCodesByLanguageName(collator.get())); } bool GetInputMethodIdsFromLanguageCode( const std::string& normalized_language_code, InputMethodType type, std::vector* out_input_method_ids) { return GetInputMethodIdsFromLanguageCodeInternal( *IdMaps::GetInstance()->language_code_to_ids, normalized_language_code, type, out_input_method_ids); } bool GetInputMethodIdsFromLanguageCodeInternal( const std::multimap& language_code_to_ids, const std::string& normalized_language_code, InputMethodType type, std::vector* out_input_method_ids) { DCHECK(out_input_method_ids); out_input_method_ids->clear(); bool result = false; std::pair range = language_code_to_ids.equal_range(normalized_language_code); for (LanguageCodeToIdsMap::const_iterator iter = range.first; iter != range.second; ++iter) { const std::string& input_method_id = iter->second; if ((type == kAllInputMethods) || IsKeyboardLayout(input_method_id)) { out_input_method_ids->push_back(input_method_id); result = true; } } if ((type == kAllInputMethods) && !result) { LOG(ERROR) << "Unknown language code: " << normalized_language_code; } return result; } void GetFirstLoginInputMethodIds( const std::string& language_code, const InputMethodDescriptor& current_input_method, std::vector* out_input_method_ids) { out_input_method_ids->clear(); // First, add the current keyboard layout (one used on the login screen). out_input_method_ids->push_back(current_input_method.id); // Second, find the most popular input method associated with the // current UI language. The input method IDs returned from // GetInputMethodIdsFromLanguageCode() are sorted by popularity, hence // our basic strategy is to pick the first one, but it's a bit more // complicated as shown below. std::string most_popular_id; std::vector input_method_ids; // This returns the input methods sorted by popularity. input_method::GetInputMethodIdsFromLanguageCode( language_code, input_method::kAllInputMethods, &input_method_ids); for (size_t i = 0; i < input_method_ids.size(); ++i) { const std::string& input_method_id = input_method_ids[i]; // Pick the first one. if (most_popular_id.empty()) most_popular_id = input_method_id; // Check if there is one that matches the current keyboard layout, but // not the current keyboard itself. This is useful if there are // multiple keyboard layout choices for one input method. For // instance, Mozc provides three choices: mozc (US keyboard), mozc-jp // (JP keyboard), mozc-dv (Dvorak). const InputMethodDescriptor* descriptor = GetInputMethodDescriptorFromId(input_method_id); if (descriptor && descriptor->id != current_input_method.id && descriptor->keyboard_layout == current_input_method.keyboard_layout) { most_popular_id = input_method_id; break; } } // Add the most popular input method ID, if it's different from the // current input method. if (most_popular_id != current_input_method.id) { out_input_method_ids->push_back(most_popular_id); } } void GetLanguageCodesFromInputMethodIds( const std::vector& input_method_ids, std::vector* out_language_codes) { out_language_codes->clear(); for (size_t i = 0; i < input_method_ids.size(); ++i) { const std::string& input_method_id = input_method_ids[i]; const InputMethodDescriptor* input_method = GetInputMethodDescriptorFromId(input_method_id); if (!input_method) { LOG(ERROR) << "Unknown input method ID: " << input_method_ids[i]; continue; } const std::string language_code = GetLanguageCodeFromDescriptor(*input_method); // Add it if it's not already present. if (std::count(out_language_codes->begin(), out_language_codes->end(), language_code) == 0) { out_language_codes->push_back(language_code); } } } void EnableInputMethods(const std::string& language_code, InputMethodType type, const std::string& initial_input_method_id) { std::vector candidates; // Add input methods associated with the language. GetInputMethodIdsFromLanguageCode(language_code, type, &candidates); // Add the hardware keyboard as well. We should always add this so users // can use the hardware keyboard on the login screen and the screen locker. candidates.push_back(GetHardwareInputMethodId()); std::vector input_method_ids; // First, add the initial input method ID, if it's requested, to // input_method_ids, so it appears first on the list of active input // methods at the input language status menu. if (!initial_input_method_id.empty()) { input_method_ids.push_back(initial_input_method_id); } // Add candidates to input_method_ids, while skipping duplicates. for (size_t i = 0; i < candidates.size(); ++i) { const std::string& candidate = candidates[i]; // Not efficient, but should be fine, as the two vectors are very // short (2-5 items). if (std::count(input_method_ids.begin(), input_method_ids.end(), candidate) == 0) { input_method_ids.push_back(candidate); } } // Update ibus-daemon setting. Here, we don't save the input method list // in the user's preferences. ImeConfigValue value; value.type = ImeConfigValue::kValueTypeStringList; value.string_list_value = input_method_ids; InputMethodLibrary* library = CrosLibrary::Get()->GetInputMethodLibrary(); library->SetImeConfig(language_prefs::kGeneralSectionName, language_prefs::kPreloadEnginesConfigName, value); // Finaly, change to the initial input method, as needed. if (!initial_input_method_id.empty()) { library->ChangeInputMethod(initial_input_method_id); } } std::string GetHardwareInputMethodId() { if (!(g_browser_process && g_browser_process->local_state())) { // This shouldn't happen but just in case. LOG(ERROR) << "Local state is not yet ready"; return GetFallbackInputMethodDescriptor().id; } PrefService* local_state = g_browser_process->local_state(); if (!local_state->FindPreference(prefs::kHardwareKeyboardLayout)) { // This could happen in unittests. We register the preference in // BrowserMain::InitializeLocalState and that method is not called during // unittests. LOG(ERROR) << prefs::kHardwareKeyboardLayout << " is not registered"; return GetFallbackInputMethodDescriptor().id; } const std::string input_method_id = local_state->GetString(prefs::kHardwareKeyboardLayout); if (input_method_id.empty()) { // This is totally fine if it's empty. The hardware keyboard layout is // not stored if startup_manifest.json (OEM customization data) is not // present (ex. Cr48 doen't have that file). return GetFallbackInputMethodDescriptor().id; } return input_method_id; } InputMethodDescriptor GetFallbackInputMethodDescriptor() { return InputMethodDescriptor("xkb:us::eng", "USA", "us", "eng"); } void ReloadInternalMaps() { IdMaps::GetInstance()->ReloadMaps(); } void OnLocaleChanged() { ReloadInternalMaps(); } } // namespace input_method } // namespace chromeos