1/**************************************************************************/
2/* import_defaults_editor.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "import_defaults_editor.h"
32
33#include "core/config/project_settings.h"
34#include "core/io/resource_importer.h"
35#include "editor/action_map_editor.h"
36#include "editor/editor_autoload_settings.h"
37#include "editor/editor_plugin_settings.h"
38#include "editor/editor_sectioned_inspector.h"
39#include "editor/localization_editor.h"
40#include "editor/shader_globals_editor.h"
41#include "scene/gui/center_container.h"
42
43class ImportDefaultsEditorSettings : public Object {
44 GDCLASS(ImportDefaultsEditorSettings, Object)
45 friend class ImportDefaultsEditor;
46 List<PropertyInfo> properties;
47 HashMap<StringName, Variant> values;
48 HashMap<StringName, Variant> default_values;
49
50 Ref<ResourceImporter> importer;
51
52protected:
53 bool _set(const StringName &p_name, const Variant &p_value) {
54 if (values.has(p_name)) {
55 values[p_name] = p_value;
56 return true;
57 } else {
58 return false;
59 }
60 }
61 bool _get(const StringName &p_name, Variant &r_ret) const {
62 if (values.has(p_name)) {
63 r_ret = values[p_name];
64 return true;
65 } else {
66 r_ret = Variant();
67 return false;
68 }
69 }
70 void _get_property_list(List<PropertyInfo> *p_list) const {
71 if (importer.is_null()) {
72 return;
73 }
74 for (const PropertyInfo &E : properties) {
75 if (importer->get_option_visibility("", E.name, values)) {
76 p_list->push_back(E);
77 }
78 }
79 }
80};
81
82void ImportDefaultsEditor::_notification(int p_what) {
83 switch (p_what) {
84 case NOTIFICATION_PREDELETE: {
85 inspector->edit(nullptr);
86 } break;
87 }
88}
89
90void ImportDefaultsEditor::_reset() {
91 if (settings->importer.is_valid()) {
92 settings->values = settings->default_values;
93 settings->notify_property_list_changed();
94 }
95}
96
97void ImportDefaultsEditor::_save() {
98 if (settings->importer.is_valid()) {
99 Dictionary modified;
100
101 for (const KeyValue<StringName, Variant> &E : settings->values) {
102 if (E.value != settings->default_values[E.key]) {
103 modified[E.key] = E.value;
104 }
105 }
106
107 if (modified.size()) {
108 ProjectSettings::get_singleton()->set("importer_defaults/" + settings->importer->get_importer_name(), modified);
109 } else {
110 ProjectSettings::get_singleton()->set("importer_defaults/" + settings->importer->get_importer_name(), Variant());
111 }
112 }
113}
114
115void ImportDefaultsEditor::_update_importer() {
116 List<Ref<ResourceImporter>> importer_list;
117 ResourceFormatImporter::get_singleton()->get_importers(&importer_list);
118 Ref<ResourceImporter> importer;
119 for (const Ref<ResourceImporter> &E : importer_list) {
120 if (E->get_visible_name() == importers->get_item_text(importers->get_selected())) {
121 importer = E;
122 break;
123 }
124 }
125
126 settings->properties.clear();
127 settings->values.clear();
128 settings->importer = importer;
129
130 if (importer.is_valid()) {
131 List<ResourceImporter::ImportOption> options;
132 importer->get_import_options("", &options);
133 Dictionary d;
134 if (ProjectSettings::get_singleton()->has_setting("importer_defaults/" + importer->get_importer_name())) {
135 d = GLOBAL_GET("importer_defaults/" + importer->get_importer_name());
136 }
137
138 for (const ResourceImporter::ImportOption &E : options) {
139 settings->properties.push_back(E.option);
140 if (d.has(E.option.name)) {
141 settings->values[E.option.name] = d[E.option.name];
142 } else {
143 settings->values[E.option.name] = E.default_value;
144 }
145 settings->default_values[E.option.name] = E.default_value;
146 }
147
148 save_defaults->set_disabled(false);
149 reset_defaults->set_disabled(false);
150
151 } else {
152 save_defaults->set_disabled(true);
153 reset_defaults->set_disabled(true);
154 }
155
156 settings->notify_property_list_changed();
157
158 // Set the importer class to fetch the correct class in the XML class reference.
159 // This allows tooltips to display when hovering properties.
160 inspector->set_object_class(importer->get_class_name());
161 inspector->edit(settings);
162}
163
164void ImportDefaultsEditor::_importer_selected(int p_index) {
165 _update_importer();
166}
167
168void ImportDefaultsEditor::clear() {
169 String last_selected;
170
171 if (importers->get_selected() >= 0) {
172 last_selected = importers->get_item_text(importers->get_selected());
173 }
174
175 importers->clear();
176
177 List<Ref<ResourceImporter>> importer_list;
178 ResourceFormatImporter::get_singleton()->get_importers(&importer_list);
179 Vector<String> names;
180 for (const Ref<ResourceImporter> &E : importer_list) {
181 String vn = E->get_visible_name();
182 names.push_back(vn);
183 }
184 names.sort();
185
186 // `last_selected.is_empty()` means it's the first time being called.
187 if (last_selected.is_empty() && !names.is_empty()) {
188 last_selected = names[0];
189 }
190
191 for (int i = 0; i < names.size(); i++) {
192 importers->add_item(names[i]);
193
194 if (names[i] == last_selected) {
195 importers->select(i);
196 _update_importer();
197 }
198 }
199}
200
201ImportDefaultsEditor::ImportDefaultsEditor() {
202 ProjectSettings::get_singleton()->add_hidden_prefix("importer_defaults/");
203
204 HBoxContainer *hb = memnew(HBoxContainer);
205 hb->add_child(memnew(Label(TTR("Importer:"))));
206 importers = memnew(OptionButton);
207 hb->add_child(importers);
208 hb->add_spacer();
209 importers->connect("item_selected", callable_mp(this, &ImportDefaultsEditor::_importer_selected));
210 reset_defaults = memnew(Button);
211 reset_defaults->set_text(TTR("Reset to Defaults"));
212 reset_defaults->set_disabled(true);
213 reset_defaults->connect("pressed", callable_mp(this, &ImportDefaultsEditor::_reset));
214 hb->add_child(reset_defaults);
215 add_child(hb);
216
217 inspector = memnew(EditorInspector);
218 add_child(inspector);
219 inspector->set_v_size_flags(SIZE_EXPAND_FILL);
220 // Make it possible to display tooltips stored in the XML class reference.
221 // The object name is set when the importer changes in `_update_importer()`.
222 inspector->set_use_doc_hints(true);
223
224 CenterContainer *cc = memnew(CenterContainer);
225 save_defaults = memnew(Button);
226 save_defaults->set_text(TTR("Save"));
227 save_defaults->connect("pressed", callable_mp(this, &ImportDefaultsEditor::_save));
228 cc->add_child(save_defaults);
229 add_child(cc);
230
231 settings = memnew(ImportDefaultsEditorSettings);
232}
233
234ImportDefaultsEditor::~ImportDefaultsEditor() {
235 memdelete(settings);
236}
237