/*************************************************************************/ /* editor_data.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 "editor_data.h" #include "core/config/project_settings.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" #include "core/io/resource_loader.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "scene/resources/packed_scene.h" void EditorHistory::cleanup_history() { for (int i = 0; i < history.size(); i++) { bool fail = false; for (int j = 0; j < history[i].path.size(); j++) { if (!history[i].path[j].ref.is_null()) { continue; } Object *obj = ObjectDB::get_instance(history[i].path[j].object); if (obj) { Node *n = Object::cast_to(obj); if (n && n->is_inside_tree()) { continue; } if (!n) { // Possibly still alive continue; } } if (j <= history[i].level) { //before or equal level, complete fail fail = true; } else { //after level, clip history.write[i].path.resize(j); } break; } if (fail) { history.remove_at(i); i--; } } if (current >= history.size()) { current = history.size() - 1; } } void EditorHistory::_add_object(ObjectID p_object, const String &p_property, int p_level_change, bool p_inspector_only) { Object *obj = ObjectDB::get_instance(p_object); ERR_FAIL_COND(!obj); RefCounted *r = Object::cast_to(obj); Obj o; if (r) { o.ref = REF(r); } o.object = p_object; o.property = p_property; o.inspector_only = p_inspector_only; History h; bool has_prev = current >= 0 && current < history.size(); if (has_prev) { history.resize(current + 1); //clip history to next } if (!p_property.is_empty() && has_prev) { //add a sub property History &pr = history.write[current]; h = pr; h.path.resize(h.level + 1); h.path.push_back(o); h.level++; } else if (p_level_change != -1 && has_prev) { //add a sub property History &pr = history.write[current]; h = pr; ERR_FAIL_INDEX(p_level_change, h.path.size()); h.level = p_level_change; } else { //add a new node h.path.push_back(o); h.level = 0; } history.push_back(h); current++; } void EditorHistory::add_object_inspector_only(ObjectID p_object) { _add_object(p_object, "", -1, true); } void EditorHistory::add_object(ObjectID p_object) { _add_object(p_object, "", -1); } void EditorHistory::add_object(ObjectID p_object, const String &p_subprop) { _add_object(p_object, p_subprop, -1); } void EditorHistory::add_object(ObjectID p_object, int p_relevel) { _add_object(p_object, "", p_relevel); } int EditorHistory::get_history_len() { return history.size(); } int EditorHistory::get_history_pos() { return current; } bool EditorHistory::is_history_obj_inspector_only(int p_obj) const { ERR_FAIL_INDEX_V(p_obj, history.size(), false); ERR_FAIL_INDEX_V(history[p_obj].level, history[p_obj].path.size(), false); return history[p_obj].path[history[p_obj].level].inspector_only; } ObjectID EditorHistory::get_history_obj(int p_obj) const { ERR_FAIL_INDEX_V(p_obj, history.size(), ObjectID()); ERR_FAIL_INDEX_V(history[p_obj].level, history[p_obj].path.size(), ObjectID()); return history[p_obj].path[history[p_obj].level].object; } bool EditorHistory::is_at_beginning() const { return current <= 0; } bool EditorHistory::is_at_end() const { return ((current + 1) >= history.size()); } bool EditorHistory::next() { cleanup_history(); if ((current + 1) < history.size()) { current++; } else { return false; } return true; } bool EditorHistory::previous() { cleanup_history(); if (current > 0) { current--; } else { return false; } return true; } bool EditorHistory::is_current_inspector_only() const { if (current < 0 || current >= history.size()) { return false; } const History &h = history[current]; return h.path[h.level].inspector_only; } ObjectID EditorHistory::get_current() { if (current < 0 || current >= history.size()) { return ObjectID(); } History &h = history.write[current]; Object *obj = ObjectDB::get_instance(h.path[h.level].object); if (!obj) { return ObjectID(); } return obj->get_instance_id(); } int EditorHistory::get_path_size() const { if (current < 0 || current >= history.size()) { return 0; } const History &h = history[current]; return h.path.size(); } ObjectID EditorHistory::get_path_object(int p_index) const { if (current < 0 || current >= history.size()) { return ObjectID(); } const History &h = history[current]; ERR_FAIL_INDEX_V(p_index, h.path.size(), ObjectID()); Object *obj = ObjectDB::get_instance(h.path[p_index].object); if (!obj) { return ObjectID(); } return obj->get_instance_id(); } String EditorHistory::get_path_property(int p_index) const { if (current < 0 || current >= history.size()) { return ""; } const History &h = history[current]; ERR_FAIL_INDEX_V(p_index, h.path.size(), ""); return h.path[p_index].property; } void EditorHistory::clear() { history.clear(); current = -1; } EditorHistory::EditorHistory() { current = -1; } EditorPlugin *EditorData::get_editor(Object *p_object) { // We need to iterate backwards so that we can check user-created plugins first. // Otherwise, it would not be possible for plugins to handle CanvasItem and Spatial nodes. for (int i = editor_plugins.size() - 1; i > -1; i--) { if (editor_plugins[i]->has_main_screen() && editor_plugins[i]->handles(p_object)) { return editor_plugins[i]; } } return nullptr; } Vector EditorData::get_subeditors(Object *p_object) { Vector sub_plugins; for (int i = editor_plugins.size() - 1; i > -1; i--) { if (!editor_plugins[i]->has_main_screen() && editor_plugins[i]->handles(p_object)) { sub_plugins.push_back(editor_plugins[i]); } } return sub_plugins; } EditorPlugin *EditorData::get_editor(String p_name) { for (int i = editor_plugins.size() - 1; i > -1; i--) { if (editor_plugins[i]->get_name() == p_name) { return editor_plugins[i]; } } return nullptr; } void EditorData::copy_object_params(Object *p_object) { clipboard.clear(); List pinfo; p_object->get_property_list(&pinfo); for (const PropertyInfo &E : pinfo) { if (!(E.usage & PROPERTY_USAGE_EDITOR) || E.name == "script" || E.name == "scripts") { continue; } PropertyData pd; pd.name = E.name; pd.value = p_object->get(pd.name); clipboard.push_back(pd); } } void EditorData::get_editor_breakpoints(List *p_breakpoints) { for (int i = 0; i < editor_plugins.size(); i++) { editor_plugins[i]->get_breakpoints(p_breakpoints); } } Dictionary EditorData::get_editor_states() const { Dictionary metadata; for (int i = 0; i < editor_plugins.size(); i++) { Dictionary state = editor_plugins[i]->get_state(); if (state.is_empty()) { continue; } metadata[editor_plugins[i]->get_name()] = state; } return metadata; } Dictionary EditorData::get_scene_editor_states(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, edited_scene.size(), Dictionary()); EditedScene es = edited_scene[p_idx]; return es.editor_states; } void EditorData::set_editor_states(const Dictionary &p_states) { List keys; p_states.get_key_list(&keys); List::Element *E = keys.front(); for (; E; E = E->next()) { String name = E->get(); int idx = -1; for (int i = 0; i < editor_plugins.size(); i++) { if (editor_plugins[i]->get_name() == name) { idx = i; break; } } if (idx == -1) { continue; } editor_plugins[idx]->set_state(p_states[name]); } } void EditorData::notify_edited_scene_changed() { for (int i = 0; i < editor_plugins.size(); i++) { editor_plugins[i]->edited_scene_changed(); editor_plugins[i]->notify_scene_changed(get_edited_scene_root()); } } void EditorData::notify_resource_saved(const Ref &p_resource) { for (int i = 0; i < editor_plugins.size(); i++) { editor_plugins[i]->notify_resource_saved(p_resource); } } void EditorData::clear_editor_states() { for (int i = 0; i < editor_plugins.size(); i++) { editor_plugins[i]->clear(); } } void EditorData::save_editor_external_data() { for (int i = 0; i < editor_plugins.size(); i++) { editor_plugins[i]->save_external_data(); } } void EditorData::apply_changes_in_editors() { for (int i = 0; i < editor_plugins.size(); i++) { editor_plugins[i]->apply_changes(); } } void EditorData::save_editor_global_states() { for (int i = 0; i < editor_plugins.size(); i++) { editor_plugins[i]->save_global_state(); } } void EditorData::restore_editor_global_states() { for (int i = 0; i < editor_plugins.size(); i++) { editor_plugins[i]->restore_global_state(); } } void EditorData::paste_object_params(Object *p_object) { ERR_FAIL_NULL(p_object); undo_redo.create_action(TTR("Paste Params")); for (const PropertyData &E : clipboard) { String name = E.name; undo_redo.add_do_property(p_object, name, E.value); undo_redo.add_undo_property(p_object, name, p_object->get(name)); } undo_redo.commit_action(); } bool EditorData::call_build() { bool result = true; for (int i = 0; i < editor_plugins.size() && result; i++) { result &= editor_plugins[i]->build(); } return result; } UndoRedo &EditorData::get_undo_redo() { return undo_redo; } void EditorData::add_undo_redo_inspector_hook_callback(Callable p_callable) { undo_redo_callbacks.push_back(p_callable); } void EditorData::remove_undo_redo_inspector_hook_callback(Callable p_callable) { undo_redo_callbacks.erase(p_callable); } const Vector EditorData::get_undo_redo_inspector_hook_callback() { return undo_redo_callbacks; } void EditorData::add_move_array_element_function(const StringName &p_class, Callable p_callable) { move_element_functions.insert(p_class, p_callable); } void EditorData::remove_move_array_element_function(const StringName &p_class) { move_element_functions.erase(p_class); } Callable EditorData::get_move_array_element_function(const StringName &p_class) const { if (move_element_functions.has(p_class)) { return move_element_functions[p_class]; } return Callable(); } void EditorData::remove_editor_plugin(EditorPlugin *p_plugin) { p_plugin->undo_redo = nullptr; editor_plugins.erase(p_plugin); } void EditorData::add_editor_plugin(EditorPlugin *p_plugin) { p_plugin->undo_redo = &undo_redo; editor_plugins.push_back(p_plugin); } int EditorData::get_editor_plugin_count() const { return editor_plugins.size(); } EditorPlugin *EditorData::get_editor_plugin(int p_idx) { ERR_FAIL_INDEX_V(p_idx, editor_plugins.size(), nullptr); return editor_plugins[p_idx]; } void EditorData::add_custom_type(const String &p_type, const String &p_inherits, const Ref