Blob Blame Raw
/* === S Y N F I G ========================================================= */
/*!	\file widget_canvastimeslider.cpp
**	\brief Canvas Time Slider Widget Implementation File
**
**	$Id$
**
**	\legal
**	......... ... 2018 Ivan Mahonin
**
**	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
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

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

#include <cmath>

#include <ETL/misc>

#include <synfig/general.h>

#include <gui/localization.h>
#include <gui/canvasview.h>
#include <gui/workarea.h>
#include <gui/workarearenderer/renderer_canvas.h>

#include "widget_canvastimeslider.h"


#endif

/* === U S I N G =========================================================== */

using namespace synfig;
using namespace studio;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

/* === E N T R Y P O I N T ================================================= */

Widget_CanvasTimeslider::Widget_CanvasTimeslider():
	tooltip(Gtk::WINDOW_POPUP)
{
	thumb.show();

	tooltip.set_type_hint(Gdk::WINDOW_TYPE_HINT_TOOLTIP);
	tooltip.set_border_width(1);
	tooltip.add(thumb);

	add_events(Gdk::POINTER_MOTION_MASK | Gdk::LEAVE_NOTIFY_MASK);
}

Widget_CanvasTimeslider::~Widget_CanvasTimeslider()
	{ set_canvas_view( CanvasView::LooseHandle() ); }

void
Widget_CanvasTimeslider::set_canvas_view(const CanvasView::LooseHandle &x)
{
	if (canvas_view == x) return;

	rendering_change.disconnect();
	lock_ducks.reset();
	canvas_view = x;
	if (canvas_view && canvas_view->get_work_area())
		rendering_change = canvas_view->get_work_area()->signal_rendering().connect(
			sigc::mem_fun(*this, &Widget_CanvasTimeslider::queue_draw) );

	queue_draw();
}

void
Widget_CanvasTimeslider::show_tooltip(const synfig::Point &p, const synfig::Point &root)
{
	Cairo::RefPtr<Cairo::ImageSurface> surface;
	if ( get_width()
	  && adj_timescale
	  && canvas_view
	  && canvas_view->get_canvas()
	  && canvas_view->get_work_area()
	  && canvas_view->get_work_area()->get_renderer_canvas() )
	{
		double x     = p[0];
		double w     = (double)get_width();
		double start = adj_timescale->get_lower();
		double end   = adj_timescale->get_upper();
		float  fps   = canvas_view->get_canvas()->rend_desc().get_frame_rate();
		if (approximate_less_lp(start, end) && approximate_greater_lp(fps, 0.f)) {
			synfig::Time time(x/w*(end - start) + start);
			time = time.round(fps);
			surface = canvas_view->get_work_area()->get_renderer_canvas()->get_thumb(time);
		}
	}

	thumb.set(surface);
	if (surface && get_screen() && get_width() > 0) {
		const int space = 20;
		int tooltip_w = surface->get_width();
		int tooltip_h = surface->get_height();

		int screen_h = get_screen()->get_height();

		int w    = get_width();
		int h    = get_height();
		int left = (int)round(root[0] - p[0]);
		int top  = (int)round(root[1] - p[1]);

		int x = left;
		if (w > tooltip_w)
			x += (int)round(p[0]/(double)w*(w - tooltip_w));

		int y = 0;
		bool visible = false;
		if (top > 2*space + tooltip_h) {
			y = top - space - tooltip_h;
			visible = true;
		} else
		if (screen_h - top - h > 2*space + tooltip_h) {
			y = top + h + space;
			visible = true;
		}

		if (visible) {
			tooltip.set_screen(get_screen());
			tooltip.move(x, y);
			tooltip.show();
		} else tooltip.hide();
	} else tooltip.hide();
}

bool
Widget_CanvasTimeslider::on_button_press_event(GdkEventButton *event)
{
	if (event->button == 1 && canvas_view && canvas_view->get_work_area()) {
		lock_ducks = new LockDucks(etl::handle<CanvasView>(canvas_view));
		canvas_view->get_work_area()->clear_ducks();
		canvas_view->queue_rebuild_ducks();
	}
	if (event->button == 1 || event->button == 2)
		tooltip.hide();
	return Widget_Timeslider::on_button_press_event(event);
}

bool
Widget_CanvasTimeslider::on_button_release_event(GdkEventButton *event)
{
	if (event->button == 1 && canvas_view)
		lock_ducks.reset();
	//if ( (event->button == 1 && !(event->state & Gdk::BUTTON2_MASK))
	//  || (event->button == 2 && !(event->state & Gdk::BUTTON1_MASK)) )
	//	show_tooltip(Point(event->x, event->y), Point(event->x_root, event->y_root));
	return Widget_Timeslider::on_button_release_event(event);
}

bool
Widget_CanvasTimeslider::on_motion_notify_event(GdkEventMotion* event)
{
	if ((event->state & (Gdk::BUTTON1_MASK | Gdk::BUTTON2_MASK)) == 0)
		show_tooltip(Point(event->x, event->y), Point(event->x_root, event->y_root));
	return Widget_Timeslider::on_motion_notify_event(event);
}

bool
Widget_CanvasTimeslider::on_leave_notify_event(GdkEventCrossing*)
{
	tooltip.hide();
	return true;
}


void
Widget_CanvasTimeslider::draw_background(const Cairo::RefPtr<Cairo::Context> &cr)
{
	Widget_Timeslider::draw_background(cr);

	if (!canvas_view || !canvas_view->get_work_area()) return;
	Canvas::Handle canvas = canvas_view->get_canvas();
	Renderer_Canvas::Handle renderer_canvas = canvas_view->get_work_area()->get_renderer_canvas();
	if (!canvas || !renderer_canvas) return;

	Glib::RefPtr<Gdk::Window> window = get_window();
	int w = get_width(), h = get_height();
	if (w == 0 || h == 0) return;

	if (!adj_timescale) return;
	double start = adj_timescale->get_lower();
	double end   = adj_timescale->get_upper();
	if (approximate_less_or_equal_lp(end, start)) return;

	RendDesc desc = canvas->rend_desc();
	float fps = desc.get_frame_rate();
	if (approximate_less_or_equal_lp(fps, 0.f)) return;
	double frame_duration = 1.0/(double)fps;

	Renderer_Canvas::StatusMap status_map;
	renderer_canvas->get_render_status(status_map);
	double k = (double)w/(end - start);
	double top = 0.0;
	double width = frame_duration*k + 1.0;
	double height = (double)h;
	for(Renderer_Canvas::StatusMap::const_iterator i = status_map.begin(); i != status_map.end(); ++i) {
		cr->save();
		switch(i->second) {
		case Renderer_Canvas::FS_PartiallyDone : cr->set_source_rgba(0.4, 0.4, 0.4, 1.0); break;
		case Renderer_Canvas::FS_InProcess     : cr->set_source_rgba(1.0, 1.0, 0.0, 1.0); break;
		case Renderer_Canvas::FS_Done          : cr->set_source_rgba(0.0, 0.8, 0.0, 1.0); break;
		default                                : cr->set_source_rgba(0.0, 0.0, 0.0, 0.0); break;
		}
		cr->rectangle(((double)i->first - start)*k, top, width, height);
		cr->fill();
		cr->restore();
	}
}