1 /*************************************************************************/
2 /* sample_library_editor_plugin.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
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 #include "sample_library_editor_plugin.h"
31
32 #include "editor/editor_settings.h"
33 #include "globals.h"
34 #include "io/resource_loader.h"
35 #include "sample_editor_plugin.h"
36 #include "scene/main/viewport.h"
37
_input_event(InputEvent p_event)38 void SampleLibraryEditor::_input_event(InputEvent p_event) {
39 }
40
_notification(int p_what)41 void SampleLibraryEditor::_notification(int p_what) {
42
43 if (p_what == NOTIFICATION_PROCESS) {
44 if (is_playing && !player->is_active()) {
45 TreeItem *tl = last_sample_playing->cast_to<TreeItem>();
46 tl->set_button(0, 0, get_icon("Play", "EditorIcons"));
47 is_playing = false;
48 set_process(false);
49 }
50 }
51
52 if (p_what == NOTIFICATION_ENTER_TREE) {
53 load->set_icon(get_icon("Folder", "EditorIcons"));
54 load->set_tooltip(TTR("Open Sample File(s)"));
55 }
56
57 if (p_what == NOTIFICATION_READY) {
58
59 // NodePath("/root")->connect("node_removed", this,"_node_removed",Vector<Variant>(),true);
60 }
61
62 if (p_what == NOTIFICATION_DRAW) {
63 }
64 }
65
_file_load_request(const DVector<String> & p_path)66 void SampleLibraryEditor::_file_load_request(const DVector<String> &p_path) {
67
68 for (int i = 0; i < p_path.size(); i++) {
69
70 String path = p_path[i];
71 Ref<Sample> sample = ResourceLoader::load(path, "Sample");
72 if (sample.is_null()) {
73 dialog->set_text(TTR("ERROR: Couldn't load sample!"));
74 dialog->set_title(TTR("Error!"));
75 //dialog->get_cancel()->set_text("Close");
76 dialog->get_ok()->set_text(TTR("Close"));
77 dialog->popup_centered_minsize();
78 return; ///beh should show an error i guess
79 }
80 String basename = path.get_file().basename();
81 String name = basename;
82 int counter = 0;
83 while (sample_library->has_sample(name)) {
84 counter++;
85 name = basename + "_" + itos(counter);
86 }
87
88 undo_redo->create_action(TTR("Add Sample"));
89 undo_redo->add_do_method(sample_library.operator->(), "add_sample", name, sample);
90 undo_redo->add_undo_method(sample_library.operator->(), "remove_sample", name);
91 undo_redo->add_do_method(this, "_update_library");
92 undo_redo->add_undo_method(this, "_update_library");
93 undo_redo->commit_action();
94 }
95 }
96
_load_pressed()97 void SampleLibraryEditor::_load_pressed() {
98
99 file->popup_centered_ratio();
100 }
101
_button_pressed(Object * p_item,int p_column,int p_id)102 void SampleLibraryEditor::_button_pressed(Object *p_item, int p_column, int p_id) {
103
104 TreeItem *ti = p_item->cast_to<TreeItem>();
105 String name = ti->get_text(0);
106
107 if (p_column == 0) { // Play/Stop
108
109 String btn_type;
110 if (!is_playing) {
111 is_playing = true;
112 btn_type = TTR("Stop");
113 player->play(name, true);
114 last_sample_playing = p_item;
115 set_process(true);
116 } else {
117 player->stop_all();
118 if (last_sample_playing != p_item) {
119 TreeItem *tl = last_sample_playing->cast_to<TreeItem>();
120 tl->set_button(p_column, 0, get_icon("Play", "EditorIcons"));
121 btn_type = TTR("Stop");
122 player->play(name, true);
123 last_sample_playing = p_item;
124 } else {
125 btn_type = TTR("Play");
126 is_playing = false;
127 }
128 }
129 ti->set_button(p_column, 0, get_icon(btn_type, "EditorIcons"));
130 } else if (p_column == 1) { // Edit
131
132 get_tree()->get_root()->get_child(0)->call("_resource_selected", sample_library->get_sample(name));
133 } else if (p_column == 6) { // Delete
134
135 ti->select(0);
136 _delete_pressed();
137 }
138 }
139
_item_edited()140 void SampleLibraryEditor::_item_edited() {
141
142 if (!tree->get_selected())
143 return;
144
145 TreeItem *s = tree->get_selected();
146
147 if (tree->get_selected_column() == 0) { // Name
148 // renamed
149 String old_name = s->get_metadata(0);
150 String new_name = s->get_text(0);
151 if (old_name == new_name)
152 return;
153
154 if (new_name == "" || new_name.find("\\") != -1 || new_name.find("/") != -1 || sample_library->has_sample(new_name)) {
155
156 s->set_text(0, old_name);
157 return;
158 }
159
160 Ref<Sample> samp = sample_library->get_sample(old_name);
161 undo_redo->create_action(TTR("Rename Sample"));
162 undo_redo->add_do_method(sample_library.operator->(), "remove_sample", old_name);
163 undo_redo->add_do_method(sample_library.operator->(), "add_sample", new_name, samp);
164 undo_redo->add_undo_method(sample_library.operator->(), "remove_sample", new_name);
165 undo_redo->add_undo_method(sample_library.operator->(), "add_sample", old_name, samp);
166 undo_redo->add_do_method(this, "_update_library");
167 undo_redo->add_undo_method(this, "_update_library");
168 undo_redo->commit_action();
169
170 } else if (tree->get_selected_column() == 3) { // Volume dB
171
172 StringName n = s->get_text(0);
173 sample_library->sample_set_volume_db(n, s->get_range(3));
174
175 } else if (tree->get_selected_column() == 4) { // Pitch scale
176
177 StringName n = s->get_text(0);
178 sample_library->sample_set_pitch_scale(n, s->get_range(4));
179
180 } else if (tree->get_selected_column() == 5) { // Priority
181
182 StringName n = s->get_text(0);
183 sample_library->sample_set_priority(n, s->get_range(5));
184 }
185 }
186
_delete_pressed()187 void SampleLibraryEditor::_delete_pressed() {
188
189 if (!tree->get_selected())
190 return;
191
192 String to_remove = tree->get_selected()->get_text(0);
193 undo_redo->create_action(TTR("Delete Sample"));
194 undo_redo->add_do_method(sample_library.operator->(), "remove_sample", to_remove);
195 undo_redo->add_undo_method(sample_library.operator->(), "add_sample", to_remove, sample_library->get_sample(to_remove));
196 undo_redo->add_do_method(this, "_update_library");
197 undo_redo->add_undo_method(this, "_update_library");
198 undo_redo->commit_action();
199 }
200
_update_library()201 void SampleLibraryEditor::_update_library() {
202
203 player->stop_all();
204
205 tree->clear();
206 tree->set_hide_root(true);
207 TreeItem *root = tree->create_item(NULL);
208
209 List<StringName> names;
210 sample_library->get_sample_list(&names);
211 names.sort_custom<StringName::AlphCompare>();
212
213 for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
214
215 TreeItem *ti = tree->create_item(root);
216
217 // Name + Play/Stop
218 ti->set_cell_mode(0, TreeItem::CELL_MODE_STRING);
219 ti->set_editable(0, true);
220 ti->set_selectable(0, true);
221 ti->set_text(0, E->get());
222 ti->set_metadata(0, E->get());
223 ti->add_button(0, get_icon("Play", "EditorIcons"));
224
225 Ref<Sample> smp = sample_library->get_sample(E->get());
226
227 // Preview/edit
228 Ref<ImageTexture> preview(memnew(ImageTexture));
229 preview->create(128, 16, Image::FORMAT_RGB);
230 SampleEditor::generate_preview_texture(smp, preview);
231 ti->set_cell_mode(1, TreeItem::CELL_MODE_ICON);
232 ti->set_selectable(1, false);
233 ti->set_editable(1, false);
234 ti->set_icon(1, preview);
235 ti->add_button(1, get_icon("Edit", "EditorIcons"));
236
237 // Format
238 ti->set_cell_mode(2, TreeItem::CELL_MODE_STRING);
239 ti->set_editable(2, false);
240 ti->set_selectable(2, false);
241 ti->set_text(2, String() + (smp != NULL ? (smp->get_format() == Sample::FORMAT_PCM16 ? TTR("16 Bits") + ", " : (smp->get_format() == Sample::FORMAT_PCM8 ? TTR("8 Bits") + ", " : "IMA-ADPCM,")) + (smp->is_stereo() ? TTR("Stereo") : TTR("Mono")) : TTR("Invalid")));
242
243 // Volume dB
244 ti->set_cell_mode(3, TreeItem::CELL_MODE_RANGE);
245 ti->set_range_config(3, -60, 24, 0.01);
246 ti->set_selectable(3, true);
247 ti->set_editable(3, true);
248 ti->set_range(3, sample_library->sample_get_volume_db(E->get()));
249
250 // Pitch scale
251 ti->set_cell_mode(4, TreeItem::CELL_MODE_RANGE);
252 ti->set_range_config(4, 0.01, 100, 0.01);
253 ti->set_selectable(4, true);
254 ti->set_editable(4, true);
255 ti->set_range(4, sample_library->sample_get_pitch_scale(E->get()));
256
257 // Priority
258 ti->set_cell_mode(5, TreeItem::CELL_MODE_RANGE);
259 ti->set_range_config(5, 0, 100, 1);
260 ti->set_selectable(5, true);
261 ti->set_editable(5, true);
262 ti->set_range(5, sample_library->sample_get_priority(E->get()));
263
264 // Delete
265 ti->set_cell_mode(6, TreeItem::CELL_MODE_STRING);
266 ti->add_button(6, get_icon("Remove", "EditorIcons"));
267 }
268
269 //player->add_sample("default",sample);
270 }
271
edit(Ref<SampleLibrary> p_sample_library)272 void SampleLibraryEditor::edit(Ref<SampleLibrary> p_sample_library) {
273
274 sample_library = p_sample_library;
275
276 if (!sample_library.is_null()) {
277 player->set_sample_library(sample_library);
278 _update_library();
279 } else {
280
281 hide();
282 }
283 }
284
get_drag_data_fw(const Point2 & p_point,Control * p_from)285 Variant SampleLibraryEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
286
287 TreeItem *ti = tree->get_item_at_pos(p_point);
288 if (!ti)
289 return Variant();
290
291 String name = ti->get_metadata(0);
292
293 RES res = sample_library->get_sample(name);
294 if (!res.is_valid())
295 return Variant();
296
297 return EditorNode::get_singleton()->drag_resource(res, p_from);
298 }
299
can_drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from) const300 bool SampleLibraryEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
301
302 Dictionary d = p_data;
303
304 if (!d.has("type"))
305 return false;
306
307 if (d.has("from") && (Object *)(d["from"]) == tree)
308 return false;
309
310 if (String(d["type"]) == "resource" && d.has("resource")) {
311 RES r = d["resource"];
312
313 Ref<Sample> sample = r;
314
315 if (sample.is_valid()) {
316
317 return true;
318 }
319 }
320
321 if (String(d["type"]) == "files") {
322
323 Vector<String> files = d["files"];
324
325 if (files.size() == 0)
326 return false;
327
328 for (int i = 0; i < files.size(); i++) {
329 String file = files[0];
330 String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
331
332 if (ftype != "Sample") {
333 return false;
334 }
335 }
336
337 return true;
338 }
339 return false;
340 }
341
drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from)342 void SampleLibraryEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
343
344 if (!can_drop_data_fw(p_point, p_data, p_from))
345 return;
346
347 Dictionary d = p_data;
348
349 if (!d.has("type"))
350 return;
351
352 if (String(d["type"]) == "resource" && d.has("resource")) {
353 RES r = d["resource"];
354
355 Ref<Sample> sample = r;
356
357 if (sample.is_valid()) {
358
359 String basename;
360 if (sample->get_name() != "") {
361 basename = sample->get_name();
362 } else if (sample->get_path().is_resource_file()) {
363 basename = sample->get_path().basename();
364 } else {
365 basename = "Sample";
366 }
367
368 String name = basename;
369 int counter = 0;
370 while (sample_library->has_sample(name)) {
371 counter++;
372 name = basename + "_" + itos(counter);
373 }
374
375 undo_redo->create_action(TTR("Add Sample"));
376 undo_redo->add_do_method(sample_library.operator->(), "add_sample", name, sample);
377 undo_redo->add_undo_method(sample_library.operator->(), "remove_sample", name);
378 undo_redo->add_do_method(this, "_update_library");
379 undo_redo->add_undo_method(this, "_update_library");
380 undo_redo->commit_action();
381 }
382 }
383
384 if (String(d["type"]) == "files") {
385
386 DVector<String> files = d["files"];
387
388 _file_load_request(files);
389 }
390 }
391
_bind_methods()392 void SampleLibraryEditor::_bind_methods() {
393
394 ObjectTypeDB::bind_method(_MD("_input_event"), &SampleLibraryEditor::_input_event);
395 ObjectTypeDB::bind_method(_MD("_load_pressed"), &SampleLibraryEditor::_load_pressed);
396 ObjectTypeDB::bind_method(_MD("_item_edited"), &SampleLibraryEditor::_item_edited);
397 ObjectTypeDB::bind_method(_MD("_delete_pressed"), &SampleLibraryEditor::_delete_pressed);
398 ObjectTypeDB::bind_method(_MD("_file_load_request"), &SampleLibraryEditor::_file_load_request);
399 ObjectTypeDB::bind_method(_MD("_update_library"), &SampleLibraryEditor::_update_library);
400 ObjectTypeDB::bind_method(_MD("_button_pressed"), &SampleLibraryEditor::_button_pressed);
401
402 ObjectTypeDB::bind_method(_MD("get_drag_data_fw"), &SampleLibraryEditor::get_drag_data_fw);
403 ObjectTypeDB::bind_method(_MD("can_drop_data_fw"), &SampleLibraryEditor::can_drop_data_fw);
404 ObjectTypeDB::bind_method(_MD("drop_data_fw"), &SampleLibraryEditor::drop_data_fw);
405 }
406
SampleLibraryEditor()407 SampleLibraryEditor::SampleLibraryEditor() {
408
409 player = memnew(SamplePlayer);
410 add_child(player);
411 add_style_override("panel", get_stylebox("panel", "Panel"));
412
413 load = memnew(Button);
414 load->set_pos(Point2(5, 5));
415 load->set_size(Size2(1, 1));
416 add_child(load);
417
418 file = memnew(EditorFileDialog);
419 add_child(file);
420 List<String> extensions;
421 ResourceLoader::get_recognized_extensions_for_type("Sample", &extensions);
422 for (int i = 0; i < extensions.size(); i++)
423 file->add_filter("*." + extensions[i]);
424 file->set_mode(EditorFileDialog::MODE_OPEN_FILES);
425
426 tree = memnew(Tree);
427 tree->set_columns(7);
428 add_child(tree);
429 tree->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 5);
430 tree->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 5);
431 tree->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 30);
432 tree->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 5);
433 tree->set_column_titles_visible(true);
434 tree->set_column_title(0, TTR("Name"));
435 tree->set_column_title(1, TTR("Preview"));
436 tree->set_column_title(2, TTR("Format"));
437 tree->set_column_title(3, "dB");
438 tree->set_column_title(4, TTR("Pitch"));
439 tree->set_column_title(5, TTR("Priority"));
440 tree->set_column_title(6, "");
441
442 tree->set_column_min_width(1, 150);
443 tree->set_column_min_width(2, 100);
444 tree->set_column_min_width(3, 50);
445 tree->set_column_min_width(4, 50);
446 tree->set_column_min_width(5, 60);
447 tree->set_column_min_width(6, 32);
448 tree->set_column_expand(1, false);
449 tree->set_column_expand(2, false);
450 tree->set_column_expand(3, false);
451 tree->set_column_expand(4, false);
452 tree->set_column_expand(5, false);
453 tree->set_column_expand(6, false);
454
455 tree->set_drag_forwarding(this);
456
457 dialog = memnew(ConfirmationDialog);
458 add_child(dialog);
459
460 tree->connect("button_pressed", this, "_button_pressed");
461 load->connect("pressed", this, "_load_pressed");
462 file->connect("files_selected", this, "_file_load_request");
463 tree->connect("item_edited", this, "_item_edited");
464
465 is_playing = false;
466 }
467
edit(Object * p_object)468 void SampleLibraryEditorPlugin::edit(Object *p_object) {
469
470 sample_library_editor->set_undo_redo(&get_undo_redo());
471 SampleLibrary *s = p_object->cast_to<SampleLibrary>();
472 if (!s)
473 return;
474
475 sample_library_editor->edit(Ref<SampleLibrary>(s));
476 }
477
handles(Object * p_object) const478 bool SampleLibraryEditorPlugin::handles(Object *p_object) const {
479
480 return p_object->is_type("SampleLibrary");
481 }
482
make_visible(bool p_visible)483 void SampleLibraryEditorPlugin::make_visible(bool p_visible) {
484
485 if (p_visible) {
486 //sample_library_editor->show();
487 button->show();
488 editor->make_bottom_panel_item_visible(sample_library_editor);
489 // sample_library_editor->set_process(true);
490 } else {
491
492 if (sample_library_editor->is_visible())
493 editor->hide_bottom_panel();
494 button->hide();
495
496 // sample_library_editor->set_process(false);
497 }
498 }
499
SampleLibraryEditorPlugin(EditorNode * p_node)500 SampleLibraryEditorPlugin::SampleLibraryEditorPlugin(EditorNode *p_node) {
501
502 editor = p_node;
503 sample_library_editor = memnew(SampleLibraryEditor);
504
505 //editor->get_viewport()->add_child(sample_library_editor);
506 sample_library_editor->set_custom_minimum_size(Size2(0, 250));
507 button = p_node->add_bottom_panel_item("SampleLibrary", sample_library_editor);
508 button->hide();
509
510 //sample_library_editor->set_area_as_parent_rect();
511 // sample_library_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END);
512 // sample_library_editor->set_margin( MARGIN_TOP, 120 );
513 //sample_library_editor->hide();
514 }
515
~SampleLibraryEditorPlugin()516 SampleLibraryEditorPlugin::~SampleLibraryEditorPlugin() {
517 }
518