/*************************************************************************/ /* script_create_dialog.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* 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 "script_create_dialog.h" #include "core/io/resource_saver.h" #include "core/project_settings.h" #include "core/script_language.h" #include "core/string_builder.h" #include "editor/create_dialog.h" #include "editor/editor_scale.h" #include "core/class_db.h" #include "core/color.h" #include "core/error_list.h" #include "core/io/resource_loader.h" #include "core/list.h" #include "core/math/vector2.h" #include "core/os/dir_access.h" #include "core/os/memory.h" #include "core/reference.h" #include "core/resource.h" #include "core/typedefs.h" #include "core/variant.h" #include "editor/editor_file_dialog.h" #include "editor/editor_settings.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/check_box.h" #include "scene/gui/control.h" #include "scene/gui/grid_container.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" #include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" #include "scene/gui/popup_menu.h" #include "scene/main/node.h" #include "scene/resources/texture.h" void ScriptCreateDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { for (int i = 0; i < ScriptServer::get_language_count(); i++) { String lang = ScriptServer::get_language(i)->get_type(); Ref lang_icon = get_icon(lang, "EditorIcons"); if (lang_icon.is_valid()) { language_menu->set_item_icon(i, lang_icon); } } String last_lang = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_language", ""); if (!last_lang.empty()) { for (int i = 0; i < language_menu->get_item_count(); i++) { if (language_menu->get_item_text(i) == last_lang) { language_menu->select(i); current_language = i; break; } } } else { language_menu->select(default_language); } path_button->set_icon(get_icon("Folder", "EditorIcons")); parent_browse_button->set_icon(get_icon("Folder", "EditorIcons")); parent_search_button->set_icon(get_icon("ClassList", "EditorIcons")); status_panel->add_style_override("panel", get_stylebox("bg", "Tree")); } break; } } void ScriptCreateDialog::_path_hbox_sorted() { if (is_visible()) { int filename_start_pos = initial_bp.rfind("/") + 1; int filename_end_pos = initial_bp.length(); if (!is_built_in) { file_path->select(filename_start_pos, filename_end_pos); } // First set cursor to the end of line to scroll LineEdit view // to the right and then set the actual cursor position. file_path->set_cursor_position(file_path->get_text().length()); file_path->set_cursor_position(filename_start_pos); file_path->grab_focus(); } } bool ScriptCreateDialog::_can_be_built_in() { return (supports_built_in && built_in_enabled); } void ScriptCreateDialog::config(const String &p_base_name, const String &p_base_path, bool p_built_in_enabled, bool p_load_enabled) { class_name->set_text(""); class_name->deselect(); parent_name->set_text(p_base_name); parent_name->deselect(); if (p_base_path != "") { initial_bp = p_base_path.get_basename(); file_path->set_text(initial_bp + "." + ScriptServer::get_language(language_menu->get_selected())->get_extension()); current_language = language_menu->get_selected(); } else { initial_bp = ""; file_path->set_text(""); } file_path->deselect(); built_in_enabled = p_built_in_enabled; load_enabled = p_load_enabled; _lang_changed(current_language); _class_name_changed(""); _path_changed(file_path->get_text()); } void ScriptCreateDialog::set_inheritance_base_type(const String &p_base) { base_type = p_base; } bool ScriptCreateDialog::_validate_parent(const String &p_string) { if (p_string.length() == 0) { return false; } if (can_inherit_from_file && p_string.is_quoted()) { String p = p_string.substr(1, p_string.length() - 2); if (_validate_path(p, true) == "") { return true; } } return ClassDB::class_exists(p_string) || ScriptServer::is_global_class(p_string); } bool ScriptCreateDialog::_validate_class(const String &p_string) { if (p_string.length() == 0) { return false; } for (int i = 0; i < p_string.length(); i++) { if (i == 0) { if (p_string[0] >= '0' && p_string[0] <= '9') { return false; // no start with number plz } } bool valid_char = (p_string[i] >= '0' && p_string[i] <= '9') || (p_string[i] >= 'a' && p_string[i] <= 'z') || (p_string[i] >= 'A' && p_string[i] <= 'Z') || p_string[i] == '_' || p_string[i] == '.'; if (!valid_char) { return false; } } return true; } String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must_exist) { String p = p_path.strip_edges(); if (p == "") { return TTR("Path is empty."); } if (p.get_file().get_basename() == "") { return TTR("Filename is empty."); } p = ProjectSettings::get_singleton()->localize_path(p); if (!p.begins_with("res://")) { return TTR("Path is not local."); } DirAccess *d = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (d->change_dir(p.get_base_dir()) != OK) { memdelete(d); return TTR("Invalid base path."); } memdelete(d); /* Does file already exist */ DirAccess *f = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (f->dir_exists(p)) { memdelete(f); return TTR("A directory with the same name exists."); } else if (p_file_must_exist && !f->file_exists(p)) { memdelete(f); return TTR("File does not exist."); } memdelete(f); /* Check file extension */ String extension = p.get_extension(); List extensions; // get all possible extensions for script for (int l = 0; l < language_menu->get_item_count(); l++) { ScriptServer::get_language(l)->get_recognized_extensions(&extensions); } bool found = false; bool match = false; int index = 0; for (List::Element *E = extensions.front(); E; E = E->next()) { if (E->get().nocasecmp_to(extension) == 0) { //FIXME (?) - changing language this way doesn't update controls, needs rework //language_menu->select(index); // change Language option by extension found = true; if (E->get() == ScriptServer::get_language(language_menu->get_selected())->get_extension()) { match = true; } break; } index++; } if (!found) { return TTR("Invalid extension."); } if (!match) { return TTR("Wrong extension chosen."); } /* Let ScriptLanguage do custom validation */ String path_error = ScriptServer::get_language(language_menu->get_selected())->validate_path(p); if (path_error != "") { return path_error; } /* All checks passed */ return ""; } String ScriptCreateDialog::_get_class_name() const { if (has_named_classes) { return class_name->get_text(); } else { return ProjectSettings::get_singleton()->localize_path(file_path->get_text()).get_file().get_basename(); } } void ScriptCreateDialog::_class_name_changed(const String &p_name) { if (_validate_class(class_name->get_text())) { is_class_name_valid = true; } else { is_class_name_valid = false; } _update_dialog(); } void ScriptCreateDialog::_parent_name_changed(const String &p_parent) { if (_validate_parent(parent_name->get_text())) { is_parent_name_valid = true; } else { is_parent_name_valid = false; } _update_dialog(); } void ScriptCreateDialog::_template_changed(int p_template) { String selected_template = p_template == 0 ? "" : template_menu->get_item_text(p_template); EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_template", selected_template); if (p_template == 0) { //default script_template = ""; return; } int selected_id = template_menu->get_selected_id(); for (int i = 0; i < template_list.size(); i++) { const ScriptTemplateInfo &sinfo = template_list[i]; if (sinfo.id == selected_id) { script_template = sinfo.dir.plus_file(sinfo.name + "." + sinfo.extension); break; } } } void ScriptCreateDialog::ok_pressed() { if (is_new_script_created) { _create_new(); } else { _load_exist(); } is_new_script_created = true; _update_dialog(); } void ScriptCreateDialog::_create_new() { String cname_param = _get_class_name(); Ref