Blame synfig-studio/src/gui/timemodel.cpp

6eb56a
/* === S Y N F I G ========================================================= */
6eb56a
/*!	\file timemodel.cpp
6eb56a
**	\brief TimeModel Implementation File
6eb56a
**
6eb56a
**	$Id$
6eb56a
**
6eb56a
**	\legal
6eb56a
**	Copyright (c) 2004 Adrian Bentley
6eb56a
**	......... ... 2018 Ivan Mahonin
6eb56a
**
6eb56a
**	This package is free software; you can redistribute it and/or
6eb56a
**	modify it under the terms of the GNU General Public License as
6eb56a
**	published by the Free Software Foundation; either version 2 of
6eb56a
**	the License, or (at your option) any later version.
6eb56a
**
6eb56a
**	This package is distributed in the hope that it will be useful,
6eb56a
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
6eb56a
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6eb56a
**	General Public License for more details.
6eb56a
**	\endlegal
6eb56a
*/
6eb56a
/* ========================================================================= */
6eb56a
6eb56a
/* === H E A D E R S ======================================================= */
6eb56a
6eb56a
#ifdef USING_PCH
6eb56a
#	include "pch.h"
6eb56a
#else
6eb56a
#ifdef HAVE_CONFIG_H
6eb56a
#	include <config.h></config.h>
6eb56a
#endif
6eb56a
6eb56a
#include <algorithm></algorithm>
6eb56a
6eb56a
#include <synfig general.h=""></synfig>
6eb56a
6eb56a
#include "app.h"
491f32
#include "helpers.h"
491f32
6eb56a
#include "timemodel.h"
6eb56a
6eb56a
#include <gui localization.h=""></gui>
6eb56a
6eb56a
#endif
6eb56a
6eb56a
/* === U S I N G =========================================================== */
6eb56a
6eb56a
using namespace synfig;
6eb56a
using namespace studio;
6eb56a
6eb56a
/* === M A C R O S ========================================================= */
6eb56a
6eb56a
/* === G L O B A L S ======================================================= */
6eb56a
6eb56a
/* === P R O C E D U R E S ================================================= */
6eb56a
6eb56a
/* === M E T H O D S ======================================================= */
6eb56a
6eb56a
/* === E N T R Y P O I N T ================================================= */
6eb56a
6eb56a
TimeModel::TimeModel():
c35ed2
	in_sync(),
6eb56a
	fps(),
6eb56a
	play_bounds_enabled(),
c35ed2
	play_repeat(),
6eb56a
	full_time_adjustment_(Gtk::Adjustment::create(0.0, 0.0, 0.0)),
6eb56a
	scroll_time_adjustment_(Gtk::Adjustment::create(0.0, 0.0, 0.0)),
6eb56a
	play_bounds_adjustment_(Gtk::Adjustment::create(0.0, 0.0, 0.0))
6eb56a
{
6eb56a
	full_time_adjustment_->signal_changed().connect(
6eb56a
		sigc::bind(sigc::mem_fun(*this, &TimeModel::on_changed), &full_time_adjustment_) );
6eb56a
	full_time_adjustment_->signal_value_changed().connect(
6eb56a
		sigc::bind(sigc::mem_fun(*this, &TimeModel::on_value_changed), &full_time_adjustment_) );
6eb56a
6eb56a
	scroll_time_adjustment_->signal_changed().connect(
6eb56a
		sigc::bind(sigc::mem_fun(*this, &TimeModel::on_changed), &scroll_time_adjustment_) );
6eb56a
	scroll_time_adjustment_->signal_value_changed().connect(
6eb56a
		sigc::bind(sigc::mem_fun(*this, &TimeModel::on_value_changed), &scroll_time_adjustment_) );
6eb56a
6eb56a
	play_bounds_adjustment_->signal_changed().connect(
6eb56a
		sigc::bind(sigc::mem_fun(*this, &TimeModel::on_changed), &play_bounds_adjustment_) );
6eb56a
	play_bounds_adjustment_->signal_value_changed().connect(
6eb56a
		sigc::bind(sigc::mem_fun(*this, &TimeModel::on_value_changed), &play_bounds_adjustment_) );
6eb56a
}
6eb56a
6eb56a
void
6eb56a
TimeModel::on_changed(Glib::RefPtr<gtk::adjustment> *source)</gtk::adjustment>
6eb56a
{
c35ed2
	if (in_sync) return;
6eb56a
	if (source == &play_bounds_adjustment_)
6eb56a
		set_play_bounds(Time((*source)->get_lower()), Time((*source)->get_upper()));
6eb56a
	sync();
6eb56a
}
6eb56a
6eb56a
void
6eb56a
TimeModel::on_value_changed(Glib::RefPtr<gtk::adjustment> *source)</gtk::adjustment>
6eb56a
{
c35ed2
	if (in_sync) return;
6eb56a
	if (source == &scroll_time_adjustment_) {
6eb56a
		Time lower((*source)->get_value());
6eb56a
		Time upper = lower + get_page_size();
6eb56a
		set_visible_bounds(lower, upper);
c35ed2
	} else
c35ed2
	if (source == &play_bounds_adjustment_) {
c35ed2
		Time t = round_time( Time((*source)->get_value()) );
c35ed2
		if (play_time == play_bounds_lower && t <= play_bounds_lower) return;
c35ed2
		if (play_time == play_bounds_upper && t >= play_bounds_upper) return;
c35ed2
		set_time(t);
6eb56a
	} else {
6eb56a
		set_time(Time((*source)->get_value()));
6eb56a
	}
6eb56a
	sync();
6eb56a
}
6eb56a
6eb56a
void
6eb56a
TimeModel::sync()
6eb56a
{
c35ed2
	in_sync = true;
c35ed2
	try {
c35ed2
		double precision = real_low_precision<double>();</double>
c35ed2
c35ed2
		Time step_increment = get_step_increment();
c35ed2
		Time page_increment = get_page_increment();
c35ed2
		Time page_size = get_page_size();
c35ed2
c35ed2
		// raise events only when all changes will done
c35ed2
		FreezeNotify freeze_full_time(full_time_adjustment());
c35ed2
		FreezeNotify freeze_scroll_time(scroll_time_adjustment());
c35ed2
		FreezeNotify freeze_play_bounds(play_bounds_adjustment());
c35ed2
c35ed2
		configure_adjustment(
c35ed2
			full_time_adjustment(),
c35ed2
			(double)time,
c35ed2
			(double)lower,
c35ed2
			(double)upper,
c35ed2
			(double)step_increment,
c35ed2
			(double)page_increment,
c35ed2
			(double)page_size,
c35ed2
			precision );
c35ed2
c35ed2
		configure_adjustment(
c35ed2
			scroll_time_adjustment(),
c35ed2
			(double)visible_lower,
c35ed2
			(double)lower,
c35ed2
			(double)upper,
c35ed2
			(double)step_increment,
c35ed2
			(double)page_increment,
c35ed2
			(double)page_size,
c35ed2
			precision );
c35ed2
c35ed2
		configure_adjustment(
c35ed2
			play_bounds_adjustment(),
c35ed2
			(double)time,
c35ed2
			(double)play_bounds_lower,
c35ed2
			(double)play_bounds_upper,
c35ed2
			(double)step_increment,
c35ed2
			(double)step_increment,
c35ed2
			0.0,
c35ed2
			precision );
c35ed2
	} catch(...) {
c35ed2
		in_sync = false;
c35ed2
		throw;
c35ed2
	}
c35ed2
	in_sync = false;
6eb56a
}
6eb56a
6eb56a
bool
6eb56a
TimeModel::set_time_silent(Time time, bool *is_play_time_changed)
6eb56a
{
6eb56a
	time = round_time(time);
6eb56a
	Time t = std::max(lower, std::min(upper, time));
6eb56a
	Time pt = std::max(play_bounds_lower, std::min(play_bounds_upper, t));
6eb56a
6eb56a
	if (this->play_time != pt) {
6eb56a
		this->play_time = pt;
6eb56a
		if (is_play_time_changed) *is_play_time_changed = true;
6eb56a
	}
6eb56a
6eb56a
	if (this->time == t) return false;
6eb56a
6eb56a
	this->time = t;
6eb56a
	return true;
6eb56a
}
6eb56a
6eb56a
bool
6eb56a
TimeModel::set_bounds_silent(Time lower, Time upper, float fps)
6eb56a
{
6eb56a
	if (approximate_less_or_equal_lp(fps, 0.f)) fps = 0.f;
6eb56a
6eb56a
	// try to keep minimum one frame range
6eb56a
	Time step = Time(fps ? 1.0/fps : 0.0);
6eb56a
	lower = round_time(lower);
6eb56a
	upper = std::max(round_time(lower + step), round_time(upper));
6eb56a
6eb56a
	if (this->lower == lower && this->upper == upper && this->fps == fps)
6eb56a
		return false;
6eb56a
6eb56a
	Time play_bounds_lower = this->play_bounds_lower <= this->lower ? lower : this->play_bounds_lower;
6eb56a
	Time play_bounds_upper = this->play_bounds_upper >= this->upper ? upper : this->play_bounds_upper;
6eb56a
6eb56a
	this->lower = lower;
6eb56a
	this->upper = upper;
6eb56a
	this->fps = fps;
6eb56a
6eb56a
	set_visible_bounds_silent(visible_lower, visible_upper);
6eb56a
	set_play_bounds_silent(play_bounds_lower, play_bounds_upper, play_bounds_enabled, play_repeat);
6eb56a
	set_time_silent(time, NULL);
6eb56a
	return true;
6eb56a
}
6eb56a
6eb56a
bool
6eb56a
TimeModel::set_visible_bounds_silent(Time lower, Time upper)
6eb56a
{
6eb56a
	// try to keep duration, minimum duration is a one frame
6eb56a
	Time duration = std::max(Time(fps ? 1.0/fps : 0.0), upper - lower);
6eb56a
6eb56a
	// this->lower bound have priority when this->upper < this->lower
6eb56a
	lower = std::max(this->lower, std::min(this->upper, lower));
6eb56a
	upper = std::max(lower, std::min(this->upper, upper));
6eb56a
6eb56a
	if (duration > Time() && this->lower < this->upper) {
6eb56a
		Time t = lower + duration;
6eb56a
		if (t > upper)
6eb56a
			upper = std::max(lower, std::min(this->upper, t));
6eb56a
		t = upper - duration;
6eb56a
		if (t < lower)
6eb56a
			lower = std::max(this->lower, t);
6eb56a
	}
6eb56a
6eb56a
	if (visible_lower == lower && visible_upper == upper) return false;
6eb56a
6eb56a
	visible_lower = lower;
6eb56a
	visible_upper = upper;
6eb56a
	return true;
6eb56a
}
6eb56a
6eb56a
bool
6eb56a
TimeModel::set_play_bounds_silent(Time lower, Time upper, bool enabled, bool repeat) {
6eb56a
	// this->lower bound have priority when this->upper < this->lower
6eb56a
	lower = std::max(this->lower, std::min(this->upper, round_time(lower)));
6eb56a
	upper = std::max(lower, std::min(this->upper, round_time(upper)));
6eb56a
6eb56a
	// try to keep minimum two frame range
6eb56a
	if (fps && this->lower < this->upper) {
6eb56a
		Time step = Time(2.0/fps);
6eb56a
		Time t = round_time(lower + step);
6eb56a
		if (t > upper)
6eb56a
			upper = std::max(lower, std::min(this->upper, t));
6eb56a
		t = round_time(upper - step);
6eb56a
		if (t < lower)
6eb56a
			lower = std::max(this->lower, t);
6eb56a
	}
6eb56a
6eb56a
	if ( play_bounds_lower == lower && play_bounds_upper == upper
6eb56a
	  && play_bounds_enabled == enabled && play_repeat == repeat )
6eb56a
		return false;
6eb56a
6eb56a
	play_bounds_lower = lower;
6eb56a
	play_bounds_upper = upper;
6eb56a
	play_bounds_enabled = enabled;
6eb56a
	play_repeat = repeat;
6eb56a
	set_time_silent(time, NULL);
6eb56a
	return true;
6eb56a
}
6eb56a
6eb56a
void
6eb56a
TimeModel::set_play_bounds_lower(const Time &lower) {
6eb56a
	// upper bound will be fixed to be >= (lower + two frames)
6eb56a
	Time step = Time(fps ? 2.0/fps : 0.0);
6eb56a
	Time t = round_time(lower);
6eb56a
	set_play_bounds(t, std::max(play_bounds_upper, t + step));
6eb56a
}
6eb56a
6eb56a
void
6eb56a
TimeModel::set_play_bounds_upper(const Time &upper) {
6eb56a
	// lower bound will be fixed to be <= (upper - two frames)
6eb56a
	Time step = Time(fps ? 2.0/fps : 0.0);
6eb56a
	Time t = round_time(upper);
6eb56a
	set_play_bounds(std::min(play_bounds_lower, t - step), t);
6eb56a
}
6eb56a
6eb56a
void
6eb56a
TimeModel::set_play_repeat(bool repeat)
6eb56a
{
6eb56a
	if (play_repeat == repeat) return;
6eb56a
	play_repeat = repeat;
6eb56a
	play_bounds_changed();
6eb56a
}
6eb56a
6eb56a
void
6eb56a
TimeModel::set_play_bounds_enabled(bool enabled)
6eb56a
{
6eb56a
	if (play_bounds_enabled == enabled) return;
6eb56a
	play_bounds_enabled = enabled;
6eb56a
	play_bounds_changed();
6eb56a
}
6eb56a
6eb56a
Real
6eb56a
TimeModel::get_zoom() const
6eb56a
	{ return (Real)get_size()/(Real)get_page_size(); }
6eb56a
6eb56a
void
6eb56a
TimeModel::set_zoom(Real x, const Time ¢er)
6eb56a
{
6eb56a
	x = std::max(Real(1.0), x);
6eb56a
	Time size = get_size();
6eb56a
	Time page_size = size/x;
6eb56a
	Time lower = center - page_size*0.5;
6eb56a
	Time upper = lower + page_size;
6eb56a
	set_visible_bounds(lower, upper);
6eb56a
}
6eb56a
6eb56a
void
6eb56a
TimeModel::zoom(Real x, const Time ¢er)
6eb56a
{
6eb56a
	// relative zoom in/out
6eb56a
	x = std::max(real_low_precision<real>(), x);</real>
b440da
	x = 1.0/x;
6eb56a
	Time lower = (visible_lower - center)*x + center;
6eb56a
	Time upper = (visible_upper - center)*x + center;
6eb56a
	set_visible_bounds(lower, upper);
6eb56a
}
6eb56a
6eb56a
bool
6eb56a
TimeModel::almost_equal(const synfig::Time &a, const synfig::Time &b, const synfig::Time &range) const
b440da
	{ return (a < b ? b - a : a - b) <= std::max(Time(), range) + get_frame_duration()*0.49999; }
6eb56a
6eb56a
Time
b440da
TimeModel::get_frame_duration() const
6eb56a
	{ return fps ? Time(1.0/fps).round(fps) : Time(); }
6eb56a
6eb56a
Time
6eb56a
TimeModel::get_page_increment() const
6eb56a
{
6eb56a
	Time s = get_step_increment();
6eb56a
	Time p = get_page_size();
6eb56a
	return std::max(s, std::min(p*0.8, p - s));
6eb56a
}