Blob Blame Raw
/* === S Y N F I G ========================================================= */
/*!	\file docks/dock_soundwave.cpp
**	\brief Dock for display a user-configurable Widget_SoundWave
**
**	$Id$
**
**	\legal
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**  ......... ... 2019 Rodolfo Ribeiro Gomes
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**	\endlegal
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "dock_soundwave.h"

#include <widgets/widget_soundwave.h>
#include <gui/canvasview.h>
#include <gui/localization.h>

#include <synfig/general.h>

#endif

using namespace studio;

Dock_SoundWave::Dock_SoundWave()
	: Dock_CanvasSpecific("soundwave", _("Sound"), Gtk::StockID("synfig-layer_other_sound")),
	  current_widget_sound(nullptr)
{
	set_use_scrolled(false);

	widget_kf_list.set_hexpand();
	widget_kf_list.show();
	widget_timeslider.set_hexpand();
	widget_timeslider.show();

	file_button.show();
	file_button.signal_file_set().connect(sigc::mem_fun(*this, &Dock_SoundWave::on_file_button_clicked));
	Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
	filter->add_mime_type("audio/*");
	file_button.set_filter(filter);

	clear_button.set_label(_("Clear"));
//	clear_button.get_style_context()->add_class("button");
	clear_button.show();
	clear_button.signal_clicked().connect(sigc::mem_fun(*this, &Dock_SoundWave::on_clear_button_clicked));

	channel_combo.show();
	channel_combo.set_tooltip_text(_("What sound channel to display"));
	channel_combo.signal_changed().connect(sigc::mem_fun(*this, &Dock_SoundWave::on_channel_combo_changed));

	label_delay.set_text(_("Delay:"));
	label_delay.show();

	delay_widget.show();
	delay_widget.signal_value_changed().connect(sigc::mem_fun(*this, &Dock_SoundWave::on_delay_changed));

	vscrollbar.set_vexpand();
	vscrollbar.set_hexpand(false);
	vscrollbar.show();
	hscrollbar.set_hexpand();
	hscrollbar.show();

	grid.set_column_homogeneous(false);
	grid.set_row_homogeneous(false);
	add(grid);

	file_settings_box.set_orientation(Gtk::ORIENTATION_HORIZONTAL);
	file_settings_box.set_homogeneous(false);
	file_settings_box.set_spacing(2);
	file_settings_box.pack_start(clear_button, false, false);
	file_settings_box.pack_start(channel_combo, false, false);
	file_settings_box.pack_start(label_delay, false, false);
	file_settings_box.pack_start(delay_widget, false, false);
	file_settings_box.hide();

	file_box.set_orientation(Gtk::ORIENTATION_HORIZONTAL);
	file_box.set_homogeneous(false);
	file_box.set_spacing(2);
	file_box.pack_start(file_button, false, false);
	file_box.pack_start(file_settings_box, false, false);

	drag_dest_set(Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_COPY);
	signal_drag_data_received().connect(sigc::mem_fun(*this,
				&Dock_SoundWave::on_drop_drag_data_received) );
}

void Dock_SoundWave::init_canvas_view_vfunc(etl::loose_handle<CanvasView> canvas_view)
{
	Widget_SoundWave *widget_sound = new Widget_SoundWave();
	widget_sound->set_time_model(canvas_view->time_model());
	widget_sound->show();
	widget_sound->set_hexpand(true);
	widget_sound->set_vexpand(true);
	widget_sound->signal_file_loaded().connect([=](const std::string &filename) {
		if (!filename.empty()) {
			file_button.set_uri(filename);
		} else {
			file_button.unselect_all();
		}
		file_settings_box.set_visible(!filename.empty());
	});
	canvas_view->set_ext_widget(get_name(), widget_sound);
}

void Dock_SoundWave::changed_canvas_view_vfunc(etl::loose_handle<CanvasView> canvas_view)
{
	std::lock_guard<std::mutex> lock(mutex);

	const std::vector<Gtk::Widget*> children = grid.get_children();
	for (Gtk::Widget * widget : children) {
		// CanvasView and Dock_SoundWave will delete widgets when needed
		grid.remove(*widget);
	}

	if( !canvas_view ) {
		widget_kf_list.set_time_model( etl::handle<TimeModel>() );
		widget_kf_list.set_canvas_interface( etl::loose_handle<synfigapp::CanvasInterface>() );

		widget_timeslider.set_canvas_view( CanvasView::Handle() );

		current_widget_sound = nullptr; // deleted by its studio::CanvasView::~CanvasView()

		file_box.hide();

		hscrollbar.unset_adjustment();
		vscrollbar.unset_adjustment();

		std::vector< Gtk::TargetEntry > targets;
		drag_dest_set(targets);
	} else {
		widget_kf_list.set_time_model(canvas_view->time_model());
		widget_kf_list.set_canvas_interface(canvas_view->canvas_interface());

		widget_timeslider.set_canvas_view(canvas_view);

		current_widget_sound = dynamic_cast<Widget_SoundWave*>( canvas_view->get_ext_widget(get_name()) );
		current_widget_sound->set_size_request(100, 100);
		current_widget_sound->set_hexpand(true);
		current_widget_sound->set_vexpand(true);

		std::string filename = current_widget_sound->get_filename();
		if (filename.empty())
			file_button.unselect_all();
		else
			file_button.set_uri(filename);

		setup_file_setting_data();

		file_settings_box.set_visible(!filename.empty());
		file_box.show();

		vscrollbar.set_adjustment(current_widget_sound->get_range_adjustment());
		hscrollbar.set_adjustment(canvas_view->time_model()->scroll_time_adjustment());

		grid.attach(widget_kf_list,        0, 0, 1, 1);
		grid.attach(widget_timeslider,     0, 1, 1, 1);
		grid.attach(*current_widget_sound, 0, 2, 1, 1);
		grid.attach(file_box,              0, 3, 1, 1);
		grid.attach(hscrollbar,            0, 4, 2, 1);
		grid.attach(vscrollbar,            1, 0, 1, 4);
		grid.show();

		std::vector< Gtk::TargetEntry > targets;
		drag_dest_set(targets);
		drag_dest_add_uri_targets();
	}

}

void Dock_SoundWave::on_drop_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int, int, const Gtk::SelectionData& selection_data, guint, guint time)
{
	bool success = false;
	const int length = selection_data.get_length();
	if((length >= 0) && (selection_data.get_format() == 8)) {
		std::string uri_list_string = selection_data.get_data_as_string();
		std::vector<std::string> uris;
		std::istringstream f(uri_list_string);
		std::string s;
		while (getline(f, s)) {
			// r-trim
			s.erase(std::find_if(s.rbegin(), s.rend(),
									[](int chr) { return !std::isspace(chr);}).base(),
			        s.end()
			       );
			uris.push_back(s);
		}

		success = load_sound_file(uris.front());
	}
	context->drag_finish(success, false, time);
}

void Dock_SoundWave::on_file_button_clicked()
{
	load_sound_file(file_button.get_uri());
}

void Dock_SoundWave::on_clear_button_clicked()
{
	std::lock_guard<std::mutex> lock(mutex);
	file_button.unselect_all();
	current_widget_sound->clear();
	file_settings_box.hide();
}

void Dock_SoundWave::on_channel_combo_changed()
{
	if (!current_widget_sound)
		return;
	std::string channel_string = channel_combo.get_active_id();
	if (channel_string.empty())
		return;
	int channel_idx = std::stoi(channel_string);
	current_widget_sound->set_channel_idx(channel_idx);
}

void Dock_SoundWave::on_delay_changed()
{
	if (current_widget_sound)
		current_widget_sound->set_delay(delay_widget.get_value());
}

bool Dock_SoundWave::load_sound_file(const std::string& filename)
{
	file_button.set_sensitive(false);
	std::lock_guard<std::mutex> lock(mutex);
	bool ok = current_widget_sound->load(filename);
	if (ok) {
		setup_file_setting_data();
		file_settings_box.show();
	} else {
		synfig::warning("Audio file not supported");
	}
	file_button.set_sensitive(true);
	return ok;
}

void Dock_SoundWave::setup_file_setting_data()
{
	channel_combo.remove_all();
	for (int n = 0; n < current_widget_sound->get_channel_number(); n++) {
		// let us be a bit user-friendly by starting index from 1 instead of 0
		std::string text = etl::strprintf(_("Channel #%i"), n+1);
		channel_combo.append(std::to_string(n), text);
	}
	channel_combo.set_active_id(std::to_string(current_widget_sound->get_channel_idx()));

	delay_widget.set_fps(current_widget_sound->get_time_model()->get_frame_rate());
	delay_widget.set_value(current_widget_sound->get_delay());
}