diff options
author | satorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-17 02:07:49 +0000 |
---|---|---|
committer | satorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-17 02:07:49 +0000 |
commit | ffd6ed4a32b41b6f6ffa76b4deefc9d67f1df220 (patch) | |
tree | 5e66ce03f07c3f666b7a046400f6e6a62510a5a7 /chrome/browser/chromeos | |
parent | ff71b339daec8ebf0a4081a54451333aa2d99b96 (diff) | |
download | chromium_src-ffd6ed4a32b41b6f6ffa76b4deefc9d67f1df220.zip chromium_src-ffd6ed4a32b41b6f6ffa76b4deefc9d67f1df220.tar.gz chromium_src-ffd6ed4a32b41b6f6ffa76b4deefc9d67f1df220.tar.bz2 |
The second step of refactoring LanguageConfigView.
No change in the logic. Just move the code around.
- Add language_config_model.cc, .h, _test.cc
- Sort function definitions and declarations so the order match in .cc and .h files.
Search for OCD in http://www.chromium.org/developers/coding-style
TEST=manually
BUG=chromium-os:4063
Review URL: http://codereview.chromium.org/2856005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50069 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/chromeos')
-rw-r--r-- | chrome/browser/chromeos/options/language_config_model.cc | 478 | ||||
-rw-r--r-- | chrome/browser/chromeos/options/language_config_model.h | 198 | ||||
-rw-r--r-- | chrome/browser/chromeos/options/language_config_model_unittest.cc (renamed from chrome/browser/chromeos/options/language_config_view_test.cc) | 2 | ||||
-rw-r--r-- | chrome/browser/chromeos/options/language_config_view.cc | 830 | ||||
-rw-r--r-- | chrome/browser/chromeos/options/language_config_view.h | 205 |
5 files changed, 876 insertions, 837 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 diff --git a/chrome/browser/chromeos/options/language_config_model.h b/chrome/browser/chromeos/options/language_config_model.h new file mode 100644 index 0000000..a0440d4 --- /dev/null +++ b/chrome/browser/chromeos/options/language_config_model.h @@ -0,0 +1,198 @@ +// 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. + +#ifndef CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CONFIG_MODEL_H_ +#define CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CONFIG_MODEL_H_ + +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "chrome/browser/language_combobox_model.h" +#include "chrome/browser/pref_member.h" +#include "chrome/browser/pref_service.h" +#include "chrome/browser/profile.h" +#include "chrome/common/notification_service.h" +#include "third_party/cros/chromeos_input_method.h" + +namespace chromeos { + +// The combobox model is used for adding languages in the language config +// view. +class AddLanguageComboboxModel : public LanguageComboboxModel { + public: + AddLanguageComboboxModel(Profile* profile, + const std::vector<std::string>& locale_codes); + // LanguageComboboxModel overrides. + virtual int GetItemCount(); + virtual std::wstring GetItemAt(int index); + + // Converts the given index (index of the items in the combobox) to the + // index of the internal language list. The returned index can be used + // for GetLocaleFromIndex() and GetLanguageNameAt(). + int GetLanguageIndex(int index) const; + + // Marks the given language code to be ignored. Ignored languages won't + // be shown in the combobox. It would be simpler if we could remove and + // add language codes from the model, but ComboboxModel does not allow + // items to be added/removed. Thus we use |ignore_set_| instead. + void SetIgnored(const std::string& language_code, bool ignored); + + private: + std::set<std::string> ignore_set_; + DISALLOW_COPY_AND_ASSIGN(AddLanguageComboboxModel); +}; + +// The model of LanguageConfigView. +class LanguageConfigModel : public NotificationObserver { + public: + LanguageConfigModel(PrefService* pref_service); + + // Initializes the model. + void Init(); + + // Counts the number of active input methods for the given language code. + size_t CountNumActiveInputMethods(const std::string& language_code); + + // Returns true if the language code is in the preferred language list. + bool HasLanguageCode(const std::string& language_code) const; + + // Adds the given language to the preferred language list, and returns + // the index of the row where the language is added. + size_t AddLanguageCode(const std::string& language_code); + + // Removes the language at the given row. + void RemoveLanguageAt(size_t row); + + // Updates Chrome's input method preferences. + void UpdateInputMethodPreferences( + const std::vector<std::string>& new_input_method_ids); + + // Deactivates the input methods for the given language code. + void DeactivateInputMethodsFor(const std::string& language_code); + + // Activates or deactivates an IME whose ID is |input_method_id|. + void SetInputMethodActivated(const std::string& input_method_id, + bool activated); + + // Returns true if an IME of |input_method_id| is activated. + bool InputMethodIsActivated(const std::string& input_method_id); + + // Gets the list of active IME IDs like "pinyin" and "m17n:ar:kbd". + void GetActiveInputMethodIds( + std::vector<std::string>* out_input_method_ids); + + // Converts an input method ID to a language code of the IME. Returns "Eng" + // when |input_method_id| is unknown. + // Example: "hangul" => "ko" + std::string GetLanguageCodeFromInputMethodId( + const std::string& input_method_id) const; + + // Converts an input method ID to a display name of the IME. Returns + // "USA" (US keyboard) when |input_method_id| is unknown. + // Examples: "pinyin" => "Pinyin" + // "m17n:ar:kbd" => "kbd (m17n)" + std::string GetInputMethodDisplayNameFromId( + const std::string& input_method_id) const; + + // Gets the list of input method ids associated with the given language + // code. The original contents of |input_method_ids| will be lost. + void GetInputMethodIdsFromLanguageCode( + const std::string& language_code, + std::vector<std::string>* input_method_ids) const; + + // Callback for |preload_engines_| pref updates. Initializes the preferred + // language codes based on the updated pref value. + void NotifyPrefChanged(); + + // NotificationObserver overrides. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + const std::string& preferred_language_code_at(size_t at) const { + return preferred_language_codes_[at]; + } + + size_t num_preferred_language_codes() const { + return preferred_language_codes_.size(); + } + + const std::string& supported_input_method_id_at(size_t at) const { + return supported_input_method_ids_[at]; + } + + size_t num_supported_input_method_ids() const { + return supported_input_method_ids_.size(); + } + + const std::vector<std::string>& supported_language_codes() const { + return supported_language_codes_; + } + + // Rewrites the language name and returns the modified version if + // necessary. Otherwise, returns the given language name as is. + // In particular, this rewrites the special language name used for input + // methods that don't fall under any other languages. + static std::wstring MaybeRewriteLanguageName( + const std::wstring& language_name); + + // Converts a language code to a language display name, using the + // current application locale. MaybeRewriteLanguageName() is called + // internally. + // Examples: "fr" => "French" + // "en-US" => "English (United States)" + static std::wstring GetLanguageDisplayNameFromCode( + const std::string& language_code); + + // Sorts the given language codes by their corresponding language names, + // using the unicode string comparator. Uses unstable sorting. + static void SortLanguageCodesByNames( + std::vector<std::string>* language_codes); + + // Sorts the given input method ids by their corresponding language names, + // using the unicode string comparator. Uses stable sorting. + static void SortInputMethodIdsByNames( + const std::map<std::string, std::string>& id_to_language_code_map, + std::vector<std::string>* input_method_ids); + + // Reorders the given input method ids for the language code. For + // example, if |language_codes| is "fr" and |input_method_ids| contains + // ["xkb:be::fra", and "xkb:fr::fra"], the list is reordered to + // ["xkb:fr::fra", and "xkb:be::fra"], so that French keyboard layout + // comes before Belgian keyboard layout. + static void ReorderInputMethodIdsForLanguageCode( + const std::string& language_code, + std::vector<std::string>* input_method_ids); + + private: + // Initializes id_to_{code,display_name}_map_ maps, + // as well as supported_{language_codes,input_method_ids}_ vectors. + void InitInputMethodIdMapsAndVectors(); + + // Adds the given language code and input method pair to the internal maps. + void AddInputMethodToMaps(const std::string& language_code, + const InputMethodDescriptor& input_method); + + PrefService* pref_service_; + // The codes of the preferred languages. + std::vector<std::string> preferred_language_codes_; + StringPrefMember preload_engines_; + std::map<std::string, std::string> id_to_language_code_map_; + std::map<std::string, std::string> id_to_display_name_map_; + // List of supported language codes like "en" and "ja". + std::vector<std::string> supported_language_codes_; + // List of supported IME IDs like "pinyin" and "m17n:ar:kbd". + std::vector<std::string> supported_input_method_ids_; + // Map from language code to associated input method IDs. + typedef std::multimap<std::string, std::string> LanguageCodeToIdsMap; + LanguageCodeToIdsMap language_code_to_ids_map_; + + DISALLOW_COPY_AND_ASSIGN(LanguageConfigModel); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_OPTIONS_LANGUAGE_CONFIG_MODEL_H_ diff --git a/chrome/browser/chromeos/options/language_config_view_test.cc b/chrome/browser/chromeos/options/language_config_model_unittest.cc index abf8db0..758488d 100644 --- a/chrome/browser/chromeos/options/language_config_view_test.cc +++ b/chrome/browser/chromeos/options/language_config_model_unittest.cc @@ -9,7 +9,7 @@ #include "app/l10n_util.h" #include "base/utf_string_conversions.h" -#include "chrome/browser/chromeos/options/language_config_view.h" +#include "chrome/browser/chromeos/options/language_config_model.h" #include "grit/generated_resources.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chrome/browser/chromeos/options/language_config_view.cc b/chrome/browser/chromeos/options/language_config_view.cc index 907190a..fddf73a 100644 --- a/chrome/browser/chromeos/options/language_config_view.cc +++ b/chrome/browser/chromeos/options/language_config_view.cc @@ -5,23 +5,16 @@ #include "chrome/browser/chromeos/options/language_config_view.h" #include <algorithm> -#include <functional> -#include <utility> -#include <vector> #include "app/l10n_util.h" -#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/options/language_chewing_config_view.h" #include "chrome/browser/chromeos/options/language_hangul_config_view.h" #include "chrome/browser/chromeos/options/language_mozc_config_view.h" #include "chrome/browser/chromeos/options/language_pinyin_config_view.h" #include "chrome/browser/chromeos/options/options_window_view.h" #include "chrome/browser/chromeos/preferences.h" -#include "chrome/browser/chromeos/status/language_menu_l10n_util.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/pref_service.h" #include "chrome/browser/profile.h" @@ -44,38 +37,6 @@ using views::GridLayout; 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 -}; - // The width of the preferred language table shown on the left side. const int kPreferredLanguageTableWidth = 300; @@ -109,58 +70,6 @@ const int kPerLanguageSingleColumnSetId = 3; } // 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); - } -} - // This is a native button associated with input method information. class InputMethodButton : public views::NativeButton { public: @@ -217,75 +126,6 @@ class InputMethodCheckbox : public views::Checkbox { DISALLOW_COPY_AND_ASSIGN(InputMethodCheckbox); }; -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, ','))); -} - LanguageConfigView::LanguageConfigView(Profile* profile) : OptionsPageView(profile), model(profile->GetPrefs()), @@ -336,11 +176,6 @@ void LanguageConfigView::ButtonPressed( } } -void LanguageConfigView::Layout() { - // Not sure why but this is needed to show contents in the dialog. - root_container_->SetBounds(0, 0, width(), height()); -} - std::wstring LanguageConfigView::GetDialogButtonLabel( MessageBoxFlags::DialogButton button) const { if (button == MessageBoxFlags::DIALOGBUTTON_OK) { @@ -354,131 +189,17 @@ std::wstring LanguageConfigView::GetWindowTitle() const { IDS_OPTIONS_SETTINGS_LANGUAGES_DIALOG_TITLE); } +void LanguageConfigView::Layout() { + // Not sure why but this is needed to show contents in the dialog. + root_container_->SetBounds(0, 0, width(), height()); +} + gfx::Size LanguageConfigView::GetPreferredSize() { return gfx::Size(views::Window::GetLocalizedContentsSize( IDS_LANGUAGES_INPUT_DIALOG_WIDTH_CHARS, IDS_LANGUAGES_INPUT_DIALOG_HEIGHT_LINES)); } -views::View* LanguageConfigView::CreatePerLanguageConfigView( - const std::string& target_language_code) { - views::View* contents = new views::View; - GridLayout* layout = new GridLayout(contents); - contents->SetLayoutManager(layout); - - // Set up column sets for the grid layout. - ColumnSet* column_set = layout->AddColumnSet(kPerLanguageTitleColumnSetId); - column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, - GridLayout::USE_PREF, 0, 0); - - column_set = layout->AddColumnSet(kPerLanguageDoubleColumnSetId); - column_set->AddPaddingColumn(0, kUnrelatedControlHorizontalSpacing); - column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, - GridLayout::USE_PREF, 0, 0); - column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing); - column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, - GridLayout::USE_PREF, 0, 0); - - column_set = layout->AddColumnSet(kPerLanguageSingleColumnSetId); - column_set->AddPaddingColumn(0, kUnrelatedControlHorizontalSpacing); - column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, - GridLayout::USE_PREF, 0, 0); - - AddUiLanguageSection(target_language_code, layout); - layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing); - AddInputMethodSection(target_language_code, layout); - - return contents; -} - -void LanguageConfigView::AddUiLanguageSection(const std::string& language_code, - views::GridLayout* layout) { - // Create the language name label. - const std::string application_locale = - g_browser_process->GetApplicationLocale(); - const string16 language_name16 = l10n_util::GetDisplayNameForLocale( - language_code, application_locale, true); - const std::wstring language_name - = LanguageConfigModel::MaybeRewriteLanguageName( - UTF16ToWide(language_name16)); - views::Label* language_name_label = new views::Label(language_name); - language_name_label->SetFont( - language_name_label->font().DeriveFont(0, gfx::Font::BOLD)); - - // Add the language name label. - layout->StartRow(0, kPerLanguageTitleColumnSetId); - layout->AddView(language_name_label); - layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); - - layout->StartRow(0, kPerLanguageSingleColumnSetId); - if (application_locale == language_code) { - layout->AddView( - new views::Label( - l10n_util::GetStringF( - IDS_OPTIONS_SETTINGS_LANGUAGES_IS_DISPLAYED_IN_THIS_LANGUAGE, - l10n_util::GetString(IDS_PRODUCT_OS_NAME)))); - } else { - UiLanguageButton* button = new UiLanguageButton( - this, l10n_util::GetStringF( - IDS_OPTIONS_SETTINGS_LANGUAGES_DISPLAY_IN_THIS_LANGUAGE, - l10n_util::GetString(IDS_PRODUCT_OS_NAME)), - language_code); - button->set_tag(kChangeUiLanguageButton); - layout->AddView(button); - } -} - -void LanguageConfigView::AddInputMethodSection( - const std::string& language_code, - views::GridLayout* layout) { - // Create the input method title label. - views::Label* input_method_title_label = new views::Label( - l10n_util::GetString( - IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD)); - input_method_title_label->SetFont( - input_method_title_label->font().DeriveFont(0, gfx::Font::BOLD)); - - // Add the input method title label. - layout->StartRow(0, kPerLanguageTitleColumnSetId); - layout->AddView(input_method_title_label); - layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); - - // Add input method names and configuration buttons. - input_method_checkboxes_.clear(); - - // Get the list of input method ids associated with the language code. - std::vector<std::string> input_method_ids; - model.GetInputMethodIdsFromLanguageCode(language_code, &input_method_ids); - - for (size_t i = 0; i < input_method_ids.size(); ++i) { - const std::string& input_method_id = input_method_ids[i]; - const std::string display_name = model.GetInputMethodDisplayNameFromId( - input_method_id); - layout->StartRow(0, kPerLanguageDoubleColumnSetId); - InputMethodCheckbox* checkbox - = new InputMethodCheckbox(UTF8ToWide(display_name), - input_method_id); - checkbox->set_listener(this); - checkbox->set_tag(kSelectInputMethodButton); - if (model.InputMethodIsActivated(input_method_id)) { - checkbox->SetChecked(true); - } - - layout->AddView(checkbox); - input_method_checkboxes_.insert(checkbox); - // Add "configure" button for the input method if we have a - // configuration dialog for it. - if (input_method_config_view_map_.count(input_method_id) > 0) { - InputMethodButton* button = new InputMethodButton( - this, - l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_CONFIGURE), - input_method_id); - button->set_tag(kConfigureInputMethodButton); - layout->AddView(button); - } - } -} - void LanguageConfigView::OnSelectionChanged() { right_container_->RemoveAllChildViews(true); // Delete the child views. @@ -524,14 +245,6 @@ std::wstring LanguageConfigView::GetText(int row, int column_id) { return L""; } -void LanguageConfigView::Show(Profile* profile, gfx::NativeWindow parent) { - UserMetrics::RecordAction(UserMetricsAction("LanguageConfigView_Open")); - views::Window* window = views::Window::CreateChromeWindow( - parent, gfx::Rect(), new LanguageConfigView(profile)); - window->SetIsAlwaysOnTop(true); - window->Show(); -} - void LanguageConfigView::SetObserver(TableModelObserver* observer) { // We don't need the observer for the table mode, since we implement the // table model as part of the LanguageConfigView class. @@ -543,6 +256,20 @@ int LanguageConfigView::RowCount() { return model.num_preferred_language_codes(); } +void LanguageConfigView::ItemChanged(views::Combobox* combobox, + int prev_index, + int new_index) { + // Ignore the first item used for showing "Add language". + if (new_index <= 0) { + return; + } + // Get the language selected. + std::string language_selected = add_language_combobox_model_-> + GetLocaleFromIndex( + add_language_combobox_model_->GetLanguageIndex(new_index)); + OnAddLanguage(language_selected); +} + void LanguageConfigView::InitControlLayout() { // Initialize the model. model.Init(); @@ -599,6 +326,14 @@ void LanguageConfigView::InitControlLayout() { } } +void LanguageConfigView::Show(Profile* profile, gfx::NativeWindow parent) { + UserMetrics::RecordAction(UserMetricsAction("LanguageConfigView_Open")); + views::Window* window = views::Window::CreateChromeWindow( + parent, gfx::Rect(), new LanguageConfigView(profile)); + window->SetIsAlwaysOnTop(true); + window->Show(); +} + void LanguageConfigView::InitInputMethodConfigViewMap() { input_method_config_view_map_["chewing"] = CreateLanguageChewingConfigView; input_method_config_view_map_["hangul"] = CreateLanguageHangulConfigView; @@ -610,63 +345,64 @@ void LanguageConfigView::InitInputMethodConfigViewMap() { // input_method_config_view_map_["mozc-jp"] = CreateLanguageMozcConfigView; } -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)); +void LanguageConfigView::OnAddLanguage(const std::string& language_code) { + // Skip if the language is already in the preferred_language_codes_. + if (model.HasLanguageCode(language_code)) { + return; } - - // 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); - } + // Activate the first input language associated with the language. We have + // to call this before the OnItemsAdded() call below so the checkbox + // for the first input language gets checked. + std::vector<std::string> input_method_ids; + model.GetInputMethodIdsFromLanguageCode(language_code, &input_method_ids); + if (!input_method_ids.empty()) { + model.SetInputMethodActivated(input_method_ids[0], true); } - // 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()); + // Append the language to the list of language codes. + const int added_at = model.AddLanguageCode(language_code); + // Notify the table that the new row added at |added_at|. + preferred_language_table_->OnItemsAdded(added_at, 1); + // For some reason, OnItemsAdded() alone does not redraw the table. Need + // to tell the table that items are changed. TODO(satorux): Investigate + // if it's a bug in TableView2. + preferred_language_table_->OnItemsChanged( + 0, model.num_preferred_language_codes()); + // Switch to the row added. + preferred_language_table_->SelectRow(added_at); + + // Mark the language to be ignored. + add_language_combobox_model_->SetIgnored(language_code, true); + ResetAddLanguageCombobox(); } -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)); +void LanguageConfigView::OnRemoveLanguage() { + const int row = preferred_language_table_->GetFirstSelectedRow(); + const std::string& language_code = model.preferred_language_code_at(row); + // Mark the language not to be ignored. + add_language_combobox_model_->SetIgnored(language_code, false); + ResetAddLanguageCombobox(); + // Deactivate the associated input methods. + model.DeactivateInputMethodsFor(language_code); + // Remove the language code and the row from the table. + model.RemoveLanguageAt(row); + preferred_language_table_->OnItemsRemoved(row, 1); + // Switch to the previous row, or the first row. + // There should be at least one row in the table. + preferred_language_table_->SelectRow(std::max(row - 1, 0)); +} + +void LanguageConfigView::ResetAddLanguageCombobox() { + // -1 to ignore "Add language". If there are more than one language, + // enable the combobox. Otherwise, disable it. + if (add_language_combobox_model_->GetItemCount() - 1 > 0) { + add_language_combobox_->SetEnabled(true); + } else { + add_language_combobox_->SetEnabled(false); + } + // Go back to the initial "Add language" state. + add_language_combobox_->ModelChanged(); + add_language_combobox_->SetSelectedItem(0); } views::View* LanguageConfigView::CreateContentsOnLeft() { @@ -748,78 +484,121 @@ views::View* LanguageConfigView::CreateContentsOnBottom() { return contents; } -void LanguageConfigView::OnAddLanguage(const std::string& language_code) { - // Skip if the language is already in the preferred_language_codes_. - if (model.HasLanguageCode(language_code)) { - return; - } - // Activate the first input language associated with the language. We have - // to call this before the OnItemsAdded() call below so the checkbox - // for the first input language gets checked. - std::vector<std::string> input_method_ids; - model.GetInputMethodIdsFromLanguageCode(language_code, &input_method_ids); - if (!input_method_ids.empty()) { - model.SetInputMethodActivated(input_method_ids[0], true); - } +views::View* LanguageConfigView::CreatePerLanguageConfigView( + const std::string& target_language_code) { + views::View* contents = new views::View; + GridLayout* layout = new GridLayout(contents); + contents->SetLayoutManager(layout); - // Append the language to the list of language codes. - const int added_at = model.AddLanguageCode(language_code); - // Notify the table that the new row added at |added_at|. - preferred_language_table_->OnItemsAdded(added_at, 1); - // For some reason, OnItemsAdded() alone does not redraw the table. Need - // to tell the table that items are changed. TODO(satorux): Investigate - // if it's a bug in TableView2. - preferred_language_table_->OnItemsChanged( - 0, model.num_preferred_language_codes()); - // Switch to the row added. - preferred_language_table_->SelectRow(added_at); + // Set up column sets for the grid layout. + ColumnSet* column_set = layout->AddColumnSet(kPerLanguageTitleColumnSetId); + column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, + GridLayout::USE_PREF, 0, 0); - // Mark the language to be ignored. - add_language_combobox_model_->SetIgnored(language_code, true); - ResetAddLanguageCombobox(); -} + column_set = layout->AddColumnSet(kPerLanguageDoubleColumnSetId); + column_set->AddPaddingColumn(0, kUnrelatedControlHorizontalSpacing); + column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, + GridLayout::USE_PREF, 0, 0); + column_set->AddPaddingColumn(0, kRelatedControlHorizontalSpacing); + column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, + GridLayout::USE_PREF, 0, 0); -void LanguageConfigView::OnRemoveLanguage() { - const int row = preferred_language_table_->GetFirstSelectedRow(); - const std::string& language_code = model.preferred_language_code_at(row); - // Mark the language not to be ignored. - add_language_combobox_model_->SetIgnored(language_code, false); - ResetAddLanguageCombobox(); - // Deactivate the associated input methods. - model.DeactivateInputMethodsFor(language_code); - // Remove the language code and the row from the table. - model.RemoveLanguageAt(row); - preferred_language_table_->OnItemsRemoved(row, 1); - // Switch to the previous row, or the first row. - // There should be at least one row in the table. - preferred_language_table_->SelectRow(std::max(row - 1, 0)); + column_set = layout->AddColumnSet(kPerLanguageSingleColumnSetId); + column_set->AddPaddingColumn(0, kUnrelatedControlHorizontalSpacing); + column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, + GridLayout::USE_PREF, 0, 0); + + AddUiLanguageSection(target_language_code, layout); + layout->AddPaddingRow(0, kUnrelatedControlVerticalSpacing); + AddInputMethodSection(target_language_code, layout); + + return contents; } -void LanguageConfigView::ResetAddLanguageCombobox() { - // -1 to ignore "Add language". If there are more than one language, - // enable the combobox. Otherwise, disable it. - if (add_language_combobox_model_->GetItemCount() - 1 > 0) { - add_language_combobox_->SetEnabled(true); +void LanguageConfigView::AddUiLanguageSection(const std::string& language_code, + views::GridLayout* layout) { + // Create the language name label. + const std::string application_locale = + g_browser_process->GetApplicationLocale(); + const string16 language_name16 = l10n_util::GetDisplayNameForLocale( + language_code, application_locale, true); + const std::wstring language_name + = LanguageConfigModel::MaybeRewriteLanguageName( + UTF16ToWide(language_name16)); + views::Label* language_name_label = new views::Label(language_name); + language_name_label->SetFont( + language_name_label->font().DeriveFont(0, gfx::Font::BOLD)); + + // Add the language name label. + layout->StartRow(0, kPerLanguageTitleColumnSetId); + layout->AddView(language_name_label); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + + layout->StartRow(0, kPerLanguageSingleColumnSetId); + if (application_locale == language_code) { + layout->AddView( + new views::Label( + l10n_util::GetStringF( + IDS_OPTIONS_SETTINGS_LANGUAGES_IS_DISPLAYED_IN_THIS_LANGUAGE, + l10n_util::GetString(IDS_PRODUCT_OS_NAME)))); } else { - add_language_combobox_->SetEnabled(false); + UiLanguageButton* button = new UiLanguageButton( + this, l10n_util::GetStringF( + IDS_OPTIONS_SETTINGS_LANGUAGES_DISPLAY_IN_THIS_LANGUAGE, + l10n_util::GetString(IDS_PRODUCT_OS_NAME)), + language_code); + button->set_tag(kChangeUiLanguageButton); + layout->AddView(button); } - // Go back to the initial "Add language" state. - add_language_combobox_->ModelChanged(); - add_language_combobox_->SetSelectedItem(0); } -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 LanguageConfigView::AddInputMethodSection( + const std::string& language_code, + views::GridLayout* layout) { + // Create the input method title label. + views::Label* input_method_title_label = new views::Label( + l10n_util::GetString( + IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD)); + input_method_title_label->SetFont( + input_method_title_label->font().DeriveFont(0, gfx::Font::BOLD)); + + // Add the input method title label. + layout->StartRow(0, kPerLanguageTitleColumnSetId); + layout->AddView(input_method_title_label); + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); + + // Add input method names and configuration buttons. + input_method_checkboxes_.clear(); + + // Get the list of input method ids associated with the language code. + std::vector<std::string> input_method_ids; + model.GetInputMethodIdsFromLanguageCode(language_code, &input_method_ids); + + for (size_t i = 0; i < input_method_ids.size(); ++i) { + const std::string& input_method_id = input_method_ids[i]; + const std::string display_name = model.GetInputMethodDisplayNameFromId( + input_method_id); + layout->StartRow(0, kPerLanguageDoubleColumnSetId); + InputMethodCheckbox* checkbox + = new InputMethodCheckbox(UTF8ToWide(display_name), + input_method_id); + checkbox->set_listener(this); + checkbox->set_tag(kSelectInputMethodButton); + if (model.InputMethodIsActivated(input_method_id)) { + checkbox->SetChecked(true); + } + + layout->AddView(checkbox); + input_method_checkboxes_.insert(checkbox); + // Add "configure" button for the input method if we have a + // configuration dialog for it. + if (input_method_config_view_map_.count(input_method_id) > 0) { + InputMethodButton* button = new InputMethodButton( + this, + l10n_util::GetString(IDS_OPTIONS_SETTINGS_LANGUAGES_CONFIGURE), + input_method_id); + button->set_tag(kConfigureInputMethodButton); + layout->AddView(button); } } } @@ -835,66 +614,6 @@ views::DialogDelegate* LanguageConfigView::CreateInputMethodConfigureView( return NULL; } -void LanguageConfigModel::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - if (type == NotificationType::PREF_CHANGED) { - NotifyPrefChanged(); - } -} - -void LanguageConfigView::ItemChanged(views::Combobox* combobox, - int prev_index, - int new_index) { - // Ignore the first item used for showing "Add language". - if (new_index <= 0) { - return; - } - // Get the language selected. - std::string language_selected = add_language_combobox_model_-> - GetLocaleFromIndex( - add_language_combobox_model_->GetLanguageIndex(new_index)); - OnAddLanguage(language_selected); -} - -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); - } -} - void LanguageConfigView::MaybeDisableLastCheckbox() { std::vector<std::string> input_method_ids; model.GetActiveInputMethodIds(&input_method_ids); @@ -916,181 +635,4 @@ void LanguageConfigView::EnableAllCheckboxes() { } } -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_); -} - -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. - } - } -} - } // namespace chromeos diff --git a/chrome/browser/chromeos/options/language_config_view.h b/chrome/browser/chromeos/options/language_config_view.h index ce0972e..932167c 100644 --- a/chrome/browser/chromeos/options/language_config_view.h +++ b/chrome/browser/chromeos/options/language_config_view.h @@ -11,11 +11,8 @@ #include <vector> #include "app/table_model.h" -#include "chrome/browser/language_combobox_model.h" -#include "chrome/browser/pref_member.h" +#include "chrome/browser/chromeos/options/language_config_model.h" #include "chrome/browser/views/options/options_page_view.h" -#include "chrome/common/notification_service.h" -#include "third_party/cros/chromeos_input_method.h" #include "views/controls/button/native_button.h" #include "views/controls/combobox/combobox.h" #include "views/controls/label.h" @@ -24,188 +21,12 @@ #include "views/grid_layout.h" #include "views/window/dialog_delegate.h" -class Profile; - namespace chromeos { class InputMethodButton; class InputMethodCheckbox; class PreferredLanguageTableModel; -// The combobox model is used for adding languages in the language config -// view. -class AddLanguageComboboxModel : public LanguageComboboxModel { - public: - AddLanguageComboboxModel(Profile* profile, - const std::vector<std::string>& locale_codes); - // LanguageComboboxModel overrides. - virtual int GetItemCount(); - virtual std::wstring GetItemAt(int index); - - // Converts the given index (index of the items in the combobox) to the - // index of the internal language list. The returned index can be used - // for GetLocaleFromIndex() and GetLanguageNameAt(). - int GetLanguageIndex(int index) const; - - // Marks the given language code to be ignored. Ignored languages won't - // be shown in the combobox. It would be simpler if we could remove and - // add language codes from the model, but ComboboxModel does not allow - // items to be added/removed. Thus we use |ignore_set_| instead. - void SetIgnored(const std::string& language_code, bool ignored); - - private: - std::set<std::string> ignore_set_; - DISALLOW_COPY_AND_ASSIGN(AddLanguageComboboxModel); -}; - -// The model of LanguageConfigView. -class LanguageConfigModel : public NotificationObserver { - public: - LanguageConfigModel(PrefService* pref_service); - - // Initializes the model. - void Init(); - - // Gets the list of active IME IDs like "pinyin" and "m17n:ar:kbd". - void GetActiveInputMethodIds( - std::vector<std::string>* out_input_method_ids); - - // Converts an input method ID to a language code of the IME. Returns "Eng" - // when |input_method_id| is unknown. - // Example: "hangul" => "ko" - std::string GetLanguageCodeFromInputMethodId( - const std::string& input_method_id) const; - - // Converts an input method ID to a display name of the IME. Returns - // "USA" (US keyboard) when |input_method_id| is unknown. - // Examples: "pinyin" => "Pinyin" - // "m17n:ar:kbd" => "kbd (m17n)" - std::string GetInputMethodDisplayNameFromId( - const std::string& input_method_id) const; - - // Gets the list of input method ids associated with the given language - // code. The original contents of |input_method_ids| will be lost. - void GetInputMethodIdsFromLanguageCode( - const std::string& language_code, - std::vector<std::string>* input_method_ids) const; - - // Deactivates the input methods for the given language code. - void DeactivateInputMethodsFor(const std::string& language_code); - - // Activates or deactivates an IME whose ID is |input_method_id|. - void SetInputMethodActivated(const std::string& input_method_id, - bool activated); - - // Returns true if an IME of |input_method_id| is activated. - bool InputMethodIsActivated(const std::string& input_method_id); - - // Counts the number of active input methods for the given language code. - size_t CountNumActiveInputMethods(const std::string& language_code); - - // Returns true if the language code is in the preferred language list. - bool HasLanguageCode(const std::string& language_code) const; - - // Adds the given language to the preferred language list, and returns - // the index of the row where the language is added. - size_t AddLanguageCode(const std::string& language_code); - - // Removes the language at the given row. - void RemoveLanguageAt(size_t row); - - // Updates Chrome's input method preferences. - void UpdateInputMethodPreferences( - const std::vector<std::string>& new_input_method_ids); - - // Callback for |preload_engines_| pref updates. Initializes the preferred - // language codes based on the updated pref value. - void NotifyPrefChanged(); - - // NotificationObserver overrides. - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - const std::string& preferred_language_code_at(size_t at) const { - return preferred_language_codes_[at]; - } - - size_t num_preferred_language_codes() const { - return preferred_language_codes_.size(); - } - - const std::string& supported_input_method_id_at(size_t at) const { - return supported_input_method_ids_[at]; - } - - size_t num_supported_input_method_ids() const { - return supported_input_method_ids_.size(); - } - - const std::vector<std::string>& supported_language_codes() const { - return supported_language_codes_; - } - - // Rewrites the language name and returns the modified version if - // necessary. Otherwise, returns the given language name as is. - // In particular, this rewrites the special language name used for input - // methods that don't fall under any other languages. - static std::wstring MaybeRewriteLanguageName( - const std::wstring& language_name); - - // Converts a language code to a language display name, using the - // current application locale. MaybeRewriteLanguageName() is called - // internally. - // Examples: "fr" => "French" - // "en-US" => "English (United States)" - static std::wstring GetLanguageDisplayNameFromCode( - const std::string& language_code); - - // Sorts the given language codes by their corresponding language names, - // using the unicode string comparator. Uses unstable sorting. - static void SortLanguageCodesByNames( - std::vector<std::string>* language_codes); - - // Sorts the given input method ids by their corresponding language names, - // using the unicode string comparator. Uses stable sorting. - static void SortInputMethodIdsByNames( - const std::map<std::string, std::string>& id_to_language_code_map, - std::vector<std::string>* input_method_ids); - - // Reorders the given input method ids for the language code. For - // example, if |language_codes| is "fr" and |input_method_ids| contains - // ["xkb:be::fra", and "xkb:fr::fra"], the list is reordered to - // ["xkb:fr::fra", and "xkb:be::fra"], so that French keyboard layout - // comes before Belgian keyboard layout. - static void ReorderInputMethodIdsForLanguageCode( - const std::string& language_code, - std::vector<std::string>* input_method_ids); - - private: - // Adds the given language code and input method pair to the internal maps. - void AddInputMethodToMaps(const std::string& language_code, - const InputMethodDescriptor& input_method); - - // Initializes id_to_{code,display_name}_map_ maps, - // as well as supported_{language_codes,input_method_ids}_ vectors. - void InitInputMethodIdMapsAndVectors(); - - PrefService* pref_service_; - // The codes of the preferred languages. - std::vector<std::string> preferred_language_codes_; - StringPrefMember preload_engines_; - std::map<std::string, std::string> id_to_language_code_map_; - std::map<std::string, std::string> id_to_display_name_map_; - // List of supported language codes like "en" and "ja". - std::vector<std::string> supported_language_codes_; - // List of supported IME IDs like "pinyin" and "m17n:ar:kbd". - std::vector<std::string> supported_input_method_ids_; - // Map from language code to associated input method IDs. - typedef std::multimap<std::string, std::string> LanguageCodeToIdsMap; - LanguageCodeToIdsMap language_code_to_ids_map_; - - DISALLOW_COPY_AND_ASSIGN(LanguageConfigModel); -}; - // A dialog box for configuring the languages. class LanguageConfigView : public TableModel, public views::ButtonListener, @@ -247,23 +68,14 @@ class LanguageConfigView : public TableModel, virtual void SetObserver(TableModelObserver* observer); virtual int RowCount(); - // Invoked when a language is added by the add combobox. - void OnAddLanguage(const std::string& language_code); - - // Invoked when a language is removed by the remove button. - void OnRemoveLanguage(); - - // Resets the add language combobox to the initial "Add language" state. - void ResetAddLanguageCombobox(); - - // OptionsPageView overrides. - virtual void InitControlLayout(); - // views::Combobox::Listener overrides: virtual void ItemChanged(views::Combobox* combobox, int prev_index, int new_index); + // OptionsPageView overrides. + virtual void InitControlLayout(); + // Shows the language config dialog in a new window. static void Show(Profile* profile, gfx::NativeWindow parent); @@ -271,6 +83,15 @@ class LanguageConfigView : public TableModel, // Initializes the input method config view. void InitInputMethodConfigViewMap(); + // Invoked when a language is added by the add combobox. + void OnAddLanguage(const std::string& language_code); + + // Invoked when a language is removed by the remove button. + void OnRemoveLanguage(); + + // Resets the add language combobox to the initial "Add language" state. + void ResetAddLanguageCombobox(); + // Creates the contents on the left, including the language table. views::View* CreateContentsOnLeft(); |