/*************************************************************************/ /* editor_autoload_settings.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2018 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 "editor_autoload_settings.h" #include "editor_node.h" #include "global_constants.h" #include "project_settings.h" #include "scene/main/viewport.h" #include "scene/resources/packed_scene.h" #define PREVIEW_LIST_MAX_SIZE 10 void EditorAutoloadSettings::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { List afn; ResourceLoader::get_recognized_extensions_for_type("Script", &afn); ResourceLoader::get_recognized_extensions_for_type("PackedScene", &afn); EditorFileDialog *file_dialog = autoload_add_path->get_file_dialog(); for (List::Element *E = afn.front(); E; E = E->next()) { file_dialog->add_filter("*." + E->get()); } } } bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, String *r_error) { if (!p_name.is_valid_identifier()) { if (r_error) *r_error = TTR("Invalid name.") + "\n" + TTR("Valid characters:") + " a-z, A-Z, 0-9 or _"; return false; } if (ClassDB::class_exists(p_name)) { if (r_error) *r_error = TTR("Invalid name. Must not collide with an existing engine class name."); return false; } for (int i = 0; i < Variant::VARIANT_MAX; i++) { if (Variant::get_type_name(Variant::Type(i)) == p_name) { if (r_error) *r_error = TTR("Invalid name. Must not collide with an existing buit-in type name."); return false; } } for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) { if (GlobalConstants::get_global_constant_name(i) == p_name) { if (r_error) *r_error = TTR("Invalid name. Must not collide with an existing global constant name."); return false; } } return true; } void EditorAutoloadSettings::_autoload_add() { autoload_add(autoload_add_name->get_text(), autoload_add_path->get_line_edit()->get_text()); autoload_add_path->get_line_edit()->set_text(""); autoload_add_name->set_text(""); } void EditorAutoloadSettings::_autoload_selected() { TreeItem *ti = tree->get_selected(); if (!ti) return; selected_autoload = "autoload/" + ti->get_text(0); } void EditorAutoloadSettings::_autoload_edited() { if (updating_autoload) return; TreeItem *ti = tree->get_edited(); int column = tree->get_edited_column(); UndoRedo *undo_redo = EditorNode::get_undo_redo(); if (column == 0) { String name = ti->get_text(0); String old_name = selected_autoload.get_slice("/", 1); if (name == old_name) return; String error; if (!_autoload_name_is_valid(name, &error)) { ti->set_text(0, old_name); EditorNode::get_singleton()->show_warning(error); return; } if (ProjectSettings::get_singleton()->has_setting("autoload/" + name)) { ti->set_text(0, old_name); EditorNode::get_singleton()->show_warning(vformat(TTR("Autoload '%s' already exists!"), name)); return; } updating_autoload = true; name = "autoload/" + name; int order = ProjectSettings::get_singleton()->get_order(selected_autoload); String path = ProjectSettings::get_singleton()->get(selected_autoload); undo_redo->create_action(TTR("Rename Autoload")); undo_redo->add_do_property(ProjectSettings::get_singleton(), name, path); undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", name, order); undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", selected_autoload); undo_redo->add_undo_property(ProjectSettings::get_singleton(), selected_autoload, path); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", selected_autoload, order); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", name); undo_redo->add_do_method(this, "call_deferred", "update_autoload"); undo_redo->add_undo_method(this, "call_deferred", "update_autoload"); undo_redo->add_do_method(this, "emit_signal", autoload_changed); undo_redo->add_undo_method(this, "emit_signal", autoload_changed); undo_redo->commit_action(); selected_autoload = name; } else if (column == 2) { updating_autoload = true; bool checked = ti->is_checked(2); String base = "autoload/" + ti->get_text(0); int order = ProjectSettings::get_singleton()->get_order(base); String path = ProjectSettings::get_singleton()->get(base); if (path.begins_with("*")) path = path.substr(1, path.length()); if (checked) path = "*" + path; undo_redo->create_action(TTR("Toggle AutoLoad Globals")); undo_redo->add_do_property(ProjectSettings::get_singleton(), base, path); undo_redo->add_undo_property(ProjectSettings::get_singleton(), base, ProjectSettings::get_singleton()->get(base)); undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", base, order); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", base, order); undo_redo->add_do_method(this, "call_deferred", "update_autoload"); undo_redo->add_undo_method(this, "call_deferred", "update_autoload"); undo_redo->add_do_method(this, "emit_signal", autoload_changed); undo_redo->add_undo_method(this, "emit_signal", autoload_changed); undo_redo->commit_action(); } updating_autoload = false; } void EditorAutoloadSettings::_autoload_button_pressed(Object *p_item, int p_column, int p_button) { TreeItem *ti = Object::cast_to(p_item); String name = "autoload/" + ti->get_text(0); UndoRedo *undo_redo = EditorNode::get_undo_redo(); switch (p_button) { case BUTTON_OPEN: { _autoload_open(ti->get_text(1)); } break; case BUTTON_MOVE_UP: case BUTTON_MOVE_DOWN: { TreeItem *swap = NULL; if (p_button == BUTTON_MOVE_UP) { swap = ti->get_prev(); } else { swap = ti->get_next(); } if (!swap) return; String swap_name = "autoload/" + swap->get_text(0); int order = ProjectSettings::get_singleton()->get_order(name); int swap_order = ProjectSettings::get_singleton()->get_order(swap_name); undo_redo->create_action(TTR("Move Autoload")); undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", name, swap_order); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", name, order); undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", swap_name, order); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", swap_name, swap_order); undo_redo->add_do_method(this, "update_autoload"); undo_redo->add_undo_method(this, "update_autoload"); undo_redo->add_do_method(this, "emit_signal", autoload_changed); undo_redo->add_undo_method(this, "emit_signal", autoload_changed); undo_redo->commit_action(); } break; case BUTTON_DELETE: { int order = ProjectSettings::get_singleton()->get_order(name); undo_redo->create_action(TTR("Remove Autoload")); undo_redo->add_do_property(ProjectSettings::get_singleton(), name, Variant()); undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, ProjectSettings::get_singleton()->get(name)); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_persisting", name, true); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", order); undo_redo->add_do_method(this, "update_autoload"); undo_redo->add_undo_method(this, "update_autoload"); undo_redo->add_do_method(this, "emit_signal", autoload_changed); undo_redo->add_undo_method(this, "emit_signal", autoload_changed); undo_redo->commit_action(); } break; } } void EditorAutoloadSettings::_autoload_activated() { TreeItem *ti = tree->get_selected(); if (!ti) return; _autoload_open(ti->get_text(1)); } void EditorAutoloadSettings::_autoload_open(const String &fpath) { if (ResourceLoader::get_resource_type(fpath) == "PackedScene") { EditorNode::get_singleton()->open_request(fpath); } else { EditorNode::get_singleton()->load_resource(fpath); } ProjectSettingsEditor::get_singleton()->hide(); } void EditorAutoloadSettings::_autoload_file_callback(const String &p_path) { autoload_add_name->set_text(p_path.get_file().get_basename()); } void EditorAutoloadSettings::update_autoload() { if (updating_autoload) return; updating_autoload = true; Map to_remove; Map to_remove_singleton; List to_add; List to_add_singleton; // Only for when the node is still the same for (List::Element *E = autoload_cache.front(); E; E = E->next()) { to_remove.insert(E->get().name, E->get()); if (E->get().is_singleton) { to_remove_singleton.insert(E->get().name, E->get()); } } autoload_cache.clear(); tree->clear(); TreeItem *root = tree->create_item(); List props; ProjectSettings::get_singleton()->get_property_list(&props); for (List::Element *E = props.front(); E; E = E->next()) { const PropertyInfo &pi = E->get(); if (!pi.name.begins_with("autoload/")) continue; String name = pi.name.get_slice("/", 1); String path = ProjectSettings::get_singleton()->get(pi.name); if (name.empty()) continue; AutoLoadInfo old_info; if (to_remove.has(name)) { old_info = to_remove[name]; } AutoLoadInfo info; info.is_singleton = path.begins_with("*"); if (info.is_singleton) { path = path.substr(1, path.length()); } info.name = name; info.path = path; info.order = ProjectSettings::get_singleton()->get_order(pi.name); if (old_info.name == info.name) { if (old_info.path == info.path) { // Still the same resource, check singleton status to_remove.erase(name); if (info.is_singleton) { if (old_info.is_singleton) { to_remove_singleton.erase(name); } else { to_add_singleton.push_back(name); } } } else { // Resource changed to_add.push_back(info); } } else { // New autoload to_add.push_back(info); } autoload_cache.push_back(info); TreeItem *item = tree->create_item(root); item->set_text(0, name); item->set_editable(0, true); item->set_text(1, path); item->set_selectable(1, true); item->set_cell_mode(2, TreeItem::CELL_MODE_CHECK); item->set_editable(2, true); item->set_text(2, TTR("Enable")); item->set_checked(2, info.is_singleton); item->add_button(3, get_icon("FileList", "EditorIcons"), BUTTON_OPEN); item->add_button(3, get_icon("MoveUp", "EditorIcons"), BUTTON_MOVE_UP); item->add_button(3, get_icon("MoveDown", "EditorIcons"), BUTTON_MOVE_DOWN); item->add_button(3, get_icon("Remove", "EditorIcons"), BUTTON_DELETE); item->set_selectable(3, false); } // Remove autoload constants for (Map::Element *E = to_remove_singleton.front(); E; E = E->next()) { for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptServer::get_language(i)->remove_named_global_constant(E->get().name); } } // Remove obsolete nodes from the tree for (Map::Element *E = to_remove.front(); E; E = E->next()) { AutoLoadInfo &info = E->get(); Node *al = get_node("/root/" + info.name); ERR_CONTINUE(!al); get_tree()->get_root()->remove_child(al); memdelete(al); } // Register new singletons already in the tree for (List::Element *E = to_add_singleton.front(); E; E = E->next()) { Node *al = get_node("/root/" + E->get()); ERR_CONTINUE(!al); for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptServer::get_language(i)->add_named_global_constant(E->get(), al); } } // Add new nodes to the tree List nodes_to_add; for (List::Element *E = to_add.front(); E; E = E->next()) { AutoLoadInfo &info = E->get(); RES res = ResourceLoader::load(info.path); ERR_EXPLAIN("Can't autoload: " + info.path); ERR_CONTINUE(res.is_null()); Node *n = NULL; if (res->is_class("PackedScene")) { Ref ps = res; n = ps->instance(); } else if (res->is_class("Script")) { Ref