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