/**************************************************************************/ /* editor_locale_dialog.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /**************************************************************************/ /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ #include "editor_locale_dialog.h" #include "core/project_settings.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "scene/gui/check_button.h" #include "scene/gui/line_edit.h" #include "scene/gui/option_button.h" #include "scene/gui/tree.h" static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) { return (c >= 'A' && c <= 'Z'); } static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) { return (c >= 'a' && c <= 'z'); } void EditorLocaleDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_filter_mode_changed"), &EditorLocaleDialog::_filter_mode_changed); ClassDB::bind_method(D_METHOD("_edit_filters"), &EditorLocaleDialog::_edit_filters); ClassDB::bind_method(D_METHOD("_toggle_advanced"), &EditorLocaleDialog::_toggle_advanced); ClassDB::bind_method(D_METHOD("_item_selected"), &EditorLocaleDialog::_item_selected); ClassDB::bind_method(D_METHOD("_filter_lang_option_changed"), &EditorLocaleDialog::_filter_lang_option_changed); ClassDB::bind_method(D_METHOD("_filter_script_option_changed"), &EditorLocaleDialog::_filter_script_option_changed); ClassDB::bind_method(D_METHOD("_filter_cnt_option_changed"), &EditorLocaleDialog::_filter_cnt_option_changed); ADD_SIGNAL(MethodInfo("locale_selected", PropertyInfo(Variant::STRING, "locale"))); } void EditorLocaleDialog::ok_pressed() { if (edit_filters->is_pressed()) { return; // Do not update, if in filter edit mode. } String locale; if (lang_code->get_text().empty()) { return; // Language code is required. } locale = lang_code->get_text(); if (!script_code->get_text().empty()) { locale += "_" + script_code->get_text(); } if (!country_code->get_text().empty()) { locale += "_" + country_code->get_text(); } if (!variant_code->get_text().empty()) { locale += "_" + variant_code->get_text(); } emit_signal("locale_selected", TranslationServer::get_singleton()->standardize_locale(locale)); hide(); } void EditorLocaleDialog::_item_selected() { if (updating_lists) { return; } if (edit_filters->is_pressed()) { return; // Do not update, if in filter edit mode. } TreeItem *l = lang_list->get_selected(); if (l) { lang_code->set_text(l->get_metadata(0).operator String()); } TreeItem *s = script_list->get_selected(); if (s) { script_code->set_text(s->get_metadata(0).operator String()); } TreeItem *c = cnt_list->get_selected(); if (c) { country_code->set_text(c->get_metadata(0).operator String()); } } void EditorLocaleDialog::_toggle_advanced(bool p_checked) { if (!p_checked) { script_code->set_text(""); variant_code->set_text(""); } _update_tree(); } void EditorLocaleDialog::_post_popup() { ConfirmationDialog::_post_popup(); if (!locale_set) { lang_code->set_text(""); script_code->set_text(""); country_code->set_text(""); variant_code->set_text(""); } edit_filters->set_pressed(false); _update_tree(); } void EditorLocaleDialog::_filter_lang_option_changed() { TreeItem *t = lang_list->get_edited(); String lang = t->get_metadata(0); bool checked = t->is_checked(0); Variant prev; Array f_lang_all; if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/language_filter")) { f_lang_all = ProjectSettings::get_singleton()->get("internationalization/locale/language_filter"); prev = f_lang_all; } int l_idx = f_lang_all.find(lang); if (checked) { if (l_idx == -1) { f_lang_all.append(lang); } } else { if (l_idx != -1) { f_lang_all.remove(l_idx); } } f_lang_all.sort(); undo_redo->create_action(TTR("Changed Locale Language Filter")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/language_filter", f_lang_all); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/language_filter", prev); undo_redo->commit_action(); } void EditorLocaleDialog::_filter_script_option_changed() { TreeItem *t = script_list->get_edited(); String script = t->get_metadata(0); bool checked = t->is_checked(0); Variant prev; Array f_script_all; if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/script_filter")) { f_script_all = ProjectSettings::get_singleton()->get("internationalization/locale/script_filter"); prev = f_script_all; } int l_idx = f_script_all.find(script); if (checked) { if (l_idx == -1) { f_script_all.append(script); } } else { if (l_idx != -1) { f_script_all.remove(l_idx); } } f_script_all.sort(); undo_redo->create_action(TTR("Changed Locale Script Filter")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/script_filter", f_script_all); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/script_filter", prev); undo_redo->commit_action(); } void EditorLocaleDialog::_filter_cnt_option_changed() { TreeItem *t = cnt_list->get_edited(); String cnt = t->get_metadata(0); bool checked = t->is_checked(0); Variant prev; Array f_cnt_all; if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/country_filter")) { f_cnt_all = ProjectSettings::get_singleton()->get("internationalization/locale/country_filter"); prev = f_cnt_all; } int l_idx = f_cnt_all.find(cnt); if (checked) { if (l_idx == -1) { f_cnt_all.append(cnt); } } else { if (l_idx != -1) { f_cnt_all.remove(l_idx); } } f_cnt_all.sort(); undo_redo->create_action(TTR("Changed Locale Country Filter")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/country_filter", f_cnt_all); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/country_filter", prev); undo_redo->commit_action(); } void EditorLocaleDialog::_filter_mode_changed(int p_mode) { int f_mode = filter_mode->get_selected_id(); Variant prev; if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter_mode")) { prev = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter_mode"); } undo_redo->create_action(TTR("Changed Locale Filter Mode")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter_mode", f_mode); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter_mode", prev); undo_redo->commit_action(); _update_tree(); } void EditorLocaleDialog::_edit_filters(bool p_checked) { _update_tree(); } void EditorLocaleDialog::_update_tree() { updating_lists = true; int filter = SHOW_ALL_LOCALES; if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter_mode")) { filter = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter_mode"); } Array f_lang_all; if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/language_filter")) { f_lang_all = ProjectSettings::get_singleton()->get("internationalization/locale/language_filter"); } Array f_cnt_all; if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/country_filter")) { f_cnt_all = ProjectSettings::get_singleton()->get("internationalization/locale/country_filter"); } Array f_script_all; if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/script_filter")) { f_script_all = ProjectSettings::get_singleton()->get("internationalization/locale/script_filter"); } bool is_edit_mode = edit_filters->is_pressed(); filter_mode->select(filter); // Hide text advanced edit and disable OK button if in filter edit mode. advanced->set_visible(!is_edit_mode); hb_locale->set_visible(!is_edit_mode && advanced->is_pressed()); vb_script_list->set_visible(advanced->is_pressed()); get_ok()->set_disabled(is_edit_mode); // Update language list. lang_list->clear(); TreeItem *l_root = lang_list->create_item(nullptr); lang_list->set_hide_root(true); Vector languages = TranslationServer::get_singleton()->get_all_languages(); for (int i = 0; i < languages.size(); i++) { if (is_edit_mode || (filter == SHOW_ALL_LOCALES) || f_lang_all.has(languages[i]) || f_lang_all.empty()) { const String &lang = TranslationServer::get_singleton()->get_language_name(languages[i]); TreeItem *t = lang_list->create_item(l_root); if (is_edit_mode) { t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); t->set_editable(0, true); t->set_checked(0, f_lang_all.has(languages[i])); } else if (lang_code->get_text() == languages[i]) { t->select(0); } t->set_text(0, vformat("%s [%s]", lang, languages[i])); t->set_metadata(0, languages[i]); } } // Update script list. script_list->clear(); TreeItem *s_root = script_list->create_item(nullptr); script_list->set_hide_root(true); if (!is_edit_mode) { TreeItem *t = script_list->create_item(s_root); t->set_text(0, "[Default]"); t->set_metadata(0, ""); } Vector scripts = TranslationServer::get_singleton()->get_all_scripts(); for (int i = 0; i < scripts.size(); i++) { if (is_edit_mode || (filter == SHOW_ALL_LOCALES) || f_script_all.has(scripts[i]) || f_script_all.empty()) { const String &script = TranslationServer::get_singleton()->get_script_name(scripts[i]); TreeItem *t = script_list->create_item(s_root); if (is_edit_mode) { t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); t->set_editable(0, true); t->set_checked(0, f_script_all.has(scripts[i])); } else if (script_code->get_text() == scripts[i]) { t->select(0); } t->set_text(0, vformat("%s [%s]", script, scripts[i])); t->set_metadata(0, scripts[i]); } } // Update country list. cnt_list->clear(); TreeItem *c_root = cnt_list->create_item(nullptr); cnt_list->set_hide_root(true); if (!is_edit_mode) { TreeItem *t = cnt_list->create_item(c_root); t->set_text(0, "[Default]"); t->set_metadata(0, ""); } Vector countries = TranslationServer::get_singleton()->get_all_countries(); for (int i = 0; i < countries.size(); i++) { if (is_edit_mode || (filter == SHOW_ALL_LOCALES) || f_cnt_all.has(countries[i]) || f_cnt_all.empty()) { const String &cnt = TranslationServer::get_singleton()->get_country_name(countries[i]); TreeItem *t = cnt_list->create_item(c_root); if (is_edit_mode) { t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); t->set_editable(0, true); t->set_checked(0, f_cnt_all.has(countries[i])); } else if (country_code->get_text() == countries[i]) { t->select(0); } t->set_text(0, vformat("%s [%s]", cnt, countries[i])); t->set_metadata(0, countries[i]); } } updating_lists = false; } void EditorLocaleDialog::set_locale(const String &p_locale) { const String &locale = TranslationServer::get_singleton()->standardize_locale(p_locale); if (locale.empty()) { locale_set = false; lang_code->set_text(""); script_code->set_text(""); country_code->set_text(""); variant_code->set_text(""); } else { locale_set = true; Vector locale_elements = p_locale.split("_"); lang_code->set_text(locale_elements[0]); if (locale_elements.size() >= 2) { if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { script_code->set_text(locale_elements[1]); advanced->set_pressed(true); } if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { country_code->set_text(locale_elements[1]); } } if (locale_elements.size() >= 3) { if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { country_code->set_text(locale_elements[2]); } else { variant_code->set_text(locale_elements[2].to_lower()); advanced->set_pressed(true); } } if (locale_elements.size() >= 4) { variant_code->set_text(locale_elements[3].to_lower()); advanced->set_pressed(true); } } } void EditorLocaleDialog::popup_locale_dialog() { popup_centered_clamped(Size2(1050, 700) * EDSCALE, 0.8); } EditorLocaleDialog::EditorLocaleDialog() { undo_redo = EditorNode::get_undo_redo(); set_title(TTR("Select a Locale")); VBoxContainer *vb = memnew(VBoxContainer); { HBoxContainer *hb_filter = memnew(HBoxContainer); { filter_mode = memnew(OptionButton); filter_mode->add_item(TTR("Show All Locales"), SHOW_ALL_LOCALES); filter_mode->set_h_size_flags(Control::SIZE_EXPAND_FILL); filter_mode->add_item(TTR("Show Selected Locales Only"), SHOW_ONLY_SELECTED_LOCALES); filter_mode->select(0); filter_mode->connect("item_selected", this, "_filter_mode_changed"); hb_filter->add_child(filter_mode); } { edit_filters = memnew(CheckButton); edit_filters->set_text("Edit Filters"); edit_filters->set_toggle_mode(true); edit_filters->set_pressed(false); edit_filters->connect("toggled", this, "_edit_filters"); hb_filter->add_child(edit_filters); } { advanced = memnew(CheckButton); advanced->set_text("Advanced"); advanced->set_toggle_mode(true); advanced->set_pressed(false); advanced->connect("toggled", this, "_toggle_advanced"); hb_filter->add_child(advanced); } vb->add_child(hb_filter); } { HBoxContainer *hb_lists = memnew(HBoxContainer); hb_lists->set_v_size_flags(Control::SIZE_EXPAND_FILL); { VBoxContainer *vb_lang_list = memnew(VBoxContainer); vb_lang_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); { Label *lang_lbl = memnew(Label); lang_lbl->set_text(TTR("Language:")); vb_lang_list->add_child(lang_lbl); } { lang_list = memnew(Tree); lang_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); lang_list->connect("cell_selected", this, "_item_selected"); lang_list->set_columns(1); lang_list->connect("item_edited", this, "_filter_lang_option_changed"); vb_lang_list->add_child(lang_list); } hb_lists->add_child(vb_lang_list); } { vb_script_list = memnew(VBoxContainer); vb_script_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); { Label *script_lbl = memnew(Label); script_lbl->set_text(TTR("Script:")); vb_script_list->add_child(script_lbl); } { script_list = memnew(Tree); script_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); script_list->connect("cell_selected", this, "_item_selected"); script_list->set_columns(1); script_list->connect("item_edited", this, "_filter_script_option_changed"); vb_script_list->add_child(script_list); } hb_lists->add_child(vb_script_list); } { VBoxContainer *vb_cnt_list = memnew(VBoxContainer); vb_cnt_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); { Label *cnt_lbl = memnew(Label); cnt_lbl->set_text(TTR("Country:")); vb_cnt_list->add_child(cnt_lbl); } { cnt_list = memnew(Tree); cnt_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); cnt_list->connect("cell_selected", this, "_item_selected"); cnt_list->set_columns(1); cnt_list->connect("item_edited", this, "_filter_cnt_option_changed"); vb_cnt_list->add_child(cnt_list); } hb_lists->add_child(vb_cnt_list); } vb->add_child(hb_lists); } { hb_locale = memnew(HBoxContainer); hb_locale->set_h_size_flags(Control::SIZE_EXPAND_FILL); { { VBoxContainer *vb_language = memnew(VBoxContainer); vb_language->set_h_size_flags(Control::SIZE_EXPAND_FILL); { Label *language_lbl = memnew(Label); language_lbl->set_text(TTR("Language")); vb_language->add_child(language_lbl); } { lang_code = memnew(LineEdit); lang_code->set_max_length(3); lang_code->set_tooltip("Language"); vb_language->add_child(lang_code); } hb_locale->add_child(vb_language); } { VBoxContainer *vb_script = memnew(VBoxContainer); vb_script->set_h_size_flags(Control::SIZE_EXPAND_FILL); { Label *script_lbl = memnew(Label); script_lbl->set_text(TTR("Script")); vb_script->add_child(script_lbl); } { script_code = memnew(LineEdit); script_code->set_max_length(4); script_code->set_tooltip("Script"); vb_script->add_child(script_code); } hb_locale->add_child(vb_script); } { VBoxContainer *vb_country = memnew(VBoxContainer); vb_country->set_h_size_flags(Control::SIZE_EXPAND_FILL); { Label *country_lbl = memnew(Label); country_lbl->set_text(TTR("Country")); vb_country->add_child(country_lbl); } { country_code = memnew(LineEdit); country_code->set_max_length(2); country_code->set_tooltip("Country"); vb_country->add_child(country_code); } hb_locale->add_child(vb_country); } { VBoxContainer *vb_variant = memnew(VBoxContainer); vb_variant->set_h_size_flags(Control::SIZE_EXPAND_FILL); { Label *variant_lbl = memnew(Label); variant_lbl->set_text(TTR("Variant")); vb_variant->add_child(variant_lbl); } { variant_code = memnew(LineEdit); variant_code->set_h_size_flags(Control::SIZE_EXPAND_FILL); variant_code->set_placeholder("Variant"); variant_code->set_tooltip("Variant"); vb_variant->add_child(variant_code); } hb_locale->add_child(vb_variant); } } vb->add_child(hb_locale); } add_child(vb); _update_tree(); get_ok()->set_text(TTR("Select")); }