Blob Blame Raw
/* === S Y N F I G ========================================================= */
/*!	\file valuenode_dynamiclist.cpp
**	\brief Implementation of the "Dynamic List" valuenode conversion.
**
**	$Id$
**
**	\legal
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2008 Chris Moore
**  Copyright (c) 2011 Carlos López
**
**	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 "valuenode_dynamiclist.h"
#include "valuenode_const.h"
#include "valuenode_composite.h"
#include <synfig/general.h>
#include <synfig/localization.h>
#include <synfig/valuenode_registry.h>
#include <synfig/exception.h>
#include <vector>
#include <list>
#include <algorithm>
#include <synfig/canvas.h>

#endif

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

using namespace std;
using namespace etl;
using namespace synfig;

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

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

REGISTER_VALUENODE(ValueNode_DynamicList, RELEASE_VERSION_0_61_06, "dynamic_list", "Dynamic List")

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

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

ValueNode_DynamicList::ListEntry::ListEntry():
	index(0)
{
}

ValueNode_DynamicList::ListEntry::ListEntry(const ValueNode::Handle &value_node):
	value_node(value_node),
	index(0)
{
}

ValueNode_DynamicList::ListEntry::ListEntry(const ValueNode::Handle &value_node,Time begin, Time end):
	value_node(value_node)
{
	add(begin,false);
	add(end,false);
	add((begin+end)*0.5,true);
}

ValueNode_DynamicList::ListEntry::ActivepointList::iterator
ValueNode_DynamicList::ListEntry::add(Time time, bool status, int priority)
{
	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList::iterator iterator;

	//! \optimize
	Activepoint ap(time,status,priority);
	ap.set_parent_index(get_index());
	ap.set_parent_value_node(get_parent_value_node());
	timing_info.push_back(ap);
	iterator iter(--iterator(timing_info.end()));
	timing_info.sort();

	return iter;
}

ValueNode_DynamicList::ListEntry::ActivepointList::iterator
ValueNode_DynamicList::ListEntry::add(const Activepoint &x)
{
	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList::iterator iterator;

	//! \optimize
	Activepoint ap(x);
	ap.set_parent_index(get_index());
	ap.set_parent_value_node(get_parent_value_node());
	timing_info.push_back(ap);
	iterator iter(--iterator(timing_info.end()));
	timing_info.sort();

	return iter;
}

void
ValueNode_DynamicList::reindex()
{
	int i(0);

	std::vector<ListEntry>::iterator iter;

	for(iter=list.begin();iter!=list.end();++iter)
	{
		assert(iter->value_node);
		if(iter->index!=i || iter->get_parent_value_node().get()!=this)
		{
			ActivepointList::iterator iter2;

			if(iter->timing_info.size()) // is this line really necessary?
			for(iter2=iter->timing_info.begin();iter2!=iter->timing_info.end();++iter2)
			{
				iter2->set_parent_index(i);
				iter2->set_parent_value_node(this);
			}
			iter->index=i;
			iter->set_parent_value_node(this);
		}
	}
}

ValueNode_DynamicList::ListEntry
ValueNode_DynamicList::create_list_entry(int index, Time time, Real origin)
{
	ValueNode_DynamicList::ListEntry ret;
	int c(link_count());
	synfig::ValueBase prev,next;

	if(c)
		index=index%c;
	else
		index=0;

	assert(index>=0);

	ret.index=index;
	ret.set_parent_value_node(this);

	if(c)
	{
		next=(*list[index].value_node)(time);

		if(index!=0)
			prev=(*list[index-1].value_node)(time);
		else
		{
			if(get_loop())
			prev=(*list[link_count()-1].value_node)(time);
			else
			{
				prev=next;
			}
		}
	}

	Type &type(get_contained_type());
	if (type == type_vector)
	{
		if(c)
		{
			Vector a(prev.get(Vector())), b(next.get(Vector()));
			ret.value_node=ValueNode_Const::create((b-a)*origin+a);
		}
		else
		{
			ret.value_node=ValueNode_Const::create(Vector());
		}
	}
	else
	if (type == type_real)
	{
		if(c)
		{
			Real a(prev.get(Real())), b(next.get(Real()));
			ret.value_node=ValueNode_Const::create((b-a)*origin+a);
		}
		else
		{
			ret.value_node=ValueNode_Const::create(Real());
		}
	}
	else
	if (type == type_color)
	{
		if(c)
		{
			Color a(prev.get(Color())), b(next.get(Color()));
			ret.value_node=ValueNode_Composite::create((b-a)*origin+a);
		}
		else
		{
			ret.value_node=ValueNode_Const::create(Color());
		}
	}
	else
	if (type == type_angle)
	{
		if(c)
		{
			Angle a(prev.get(Angle())), b(next.get(Angle()));
			ret.value_node=ValueNode_Const::create((b-a)*origin+a);
		}
		else
		{
			ret.value_node=ValueNode_Const::create(Angle());
		}
	}
	else
	if (type == type_time)
	{
		if(c)
		{
			Time a(prev.get(Time())), b(next.get(Time()));
			ret.value_node=ValueNode_Const::create((b-a)*origin+a);
		}
		else
		{
			ret.value_node=ValueNode_Const::create(Time());
		}
	}
	else
	{
		ret.value_node=ValueNode_Const::create(get_contained_type());
	}

	ret.value_node->set_parent_canvas(get_parent_canvas());
	return ret;
}

ValueNode_DynamicList::ListEntry::ActivepointList::iterator
ValueNode_DynamicList::ListEntry::find(const UniqueID& x)
{
	return std::find(timing_info.begin(),timing_info.end(),x);
}

ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
ValueNode_DynamicList::ListEntry::find(const UniqueID& x)const
{
	return std::find(timing_info.begin(),timing_info.end(),x);
}

void
ValueNode_DynamicList::ListEntry::erase(const UniqueID& x)
{
	timing_info.erase(find(x));
}

ValueNode_DynamicList::ListEntry::ActivepointList::iterator
ValueNode_DynamicList::ListEntry::find(const Time& x)
{
	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;

	ActivepointList::iterator iter;

	for(iter=timing_info.begin();iter!=timing_info.end();++iter)
		if(iter->time==x)
			return iter;

	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find():"+x.get_string());
}

ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
ValueNode_DynamicList::ListEntry::find(const Time& x)const
{
	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;

	ActivepointList::const_iterator iter;

	for(iter=timing_info.begin();iter!=timing_info.end();++iter)
		if(iter->time==x)
			return iter;

	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find()const:"+x.get_string());
}

ValueNode_DynamicList::ListEntry::ActivepointList::iterator
ValueNode_DynamicList::ListEntry::find_next(const Time& x)
{
	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;

	ActivepointList::iterator iter;

	for(iter=timing_info.begin();iter!=timing_info.end();++iter)
		if(iter->time>x)
			return iter;

	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_next():"+x.get_string());
}

ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
ValueNode_DynamicList::ListEntry::find_next(const Time& x)const
{
	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;

	ActivepointList::const_iterator iter;

	for(iter=timing_info.begin();iter!=timing_info.end();++iter)
		if(iter->time>x)
			return iter;

	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_next()const:"+x.get_string());
}

ValueNode_DynamicList::ListEntry::ActivepointList::iterator
ValueNode_DynamicList::ListEntry::find_prev(const Time& x)
{
	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;

	ActivepointList::iterator iter;
	iter=timing_info.end();
	do
	{
		--iter;
		if(iter->time<x)
			return iter;
	}
	while(iter!=timing_info.begin());

	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_prev():"+x.get_string());
}

ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
ValueNode_DynamicList::ListEntry::find_prev(const Time& x)const
{
	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;

	ActivepointList::const_iterator iter;
	iter=timing_info.end();
	do
	{
		--iter;
		if(iter->time<x)
			return iter;
	}
	while(iter!=timing_info.begin());

	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_prev()const:"+x.get_string());
}

int
ValueNode_DynamicList::ListEntry::find(const Time& begin,const Time& end,std::vector<Activepoint*>& selected)
{
	Time curr_time(begin);
	int ret(0);

	// try to grab first waypoint
	try
	{
		ActivepointList::iterator iter;
		iter=find(curr_time);
		selected.push_back(&*iter);
		ret++;
	}
	catch(...) { }

	try
	{
		ActivepointList::iterator iter;
		while(true)
		{
			iter=find_next(curr_time);
			curr_time=iter->get_time();
			if(curr_time>=end)
				break;
			selected.push_back(&*iter);
			ret++;
		}
	}
	catch(...) { }

	return ret;
}

float
ValueNode_DynamicList::ListEntry::amount_at_time(const Time &t,bool *rising)const
{
	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;

	if(timing_info.empty())
		return 1.0f;

	try
	{
		ActivepointList::const_iterator iter;
		iter=find(t);
		return iter->state?1.0f:0.0f;
	}
	catch(...) { }

	ActivepointList::const_iterator prev_iter;
	ActivepointList::const_iterator next_iter;

	try	{ prev_iter=find_prev(t); }
	catch(...) { return find_next(t)->state?1.0f:0.0f; }

	try	{ next_iter=find_next(t); }
	catch(...) { return prev_iter->state?1.0f:0.0f; }

	if(next_iter->state==prev_iter->state)
		return next_iter->state?1.0f:0.0f;

	if(rising)*rising=next_iter->state;

	if(next_iter->state==true)
		return float((t-prev_iter->time)/(next_iter->time-prev_iter->time));

	return float((next_iter->time-t)/(next_iter->time-prev_iter->time));
}

Activepoint
ValueNode_DynamicList::ListEntry::new_activepoint_at_time(const Time& time)const
{
	Activepoint activepoint;

	activepoint.set_state(status_at_time(time));
	activepoint.set_priority(0);

	return activepoint;
}

bool
ValueNode_DynamicList::ListEntry::status_at_time(const Time &t)const
{
	//typedef synfig::ValueNode_DynamicList::ListEntry::Activepoint Activepoint;
	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;

	ActivepointList::const_iterator entry_iter;
	ActivepointList::const_iterator prev_iter;
	bool state(true);

	// New "symmetric" state mechanism
	if(!timing_info.empty())
	{
		if(timing_info.size()==1)
			state=timing_info.front().state;
		else
		{
			//! \optimize Perhaps we should use a binary search...?
			// This will give us the first activepoint that is after t.
			for(entry_iter=timing_info.begin();entry_iter!=timing_info.end();++entry_iter)
			{
				if(entry_iter->time==t)
				{
					// If we hit the entry right on the nose, then we don't
					// have to do anything more
					return entry_iter->state;
				}
				if(entry_iter->time>t)
					break;

			}
			prev_iter=entry_iter;
			prev_iter--;

			// ie:
			//
			//		|-------|---t---|-------|
			//	   prev_iter^		^entry_iter

			if(entry_iter==timing_info.end())
			{
				state=prev_iter->state;
			}
			else
			if(entry_iter==timing_info.begin())
			{
				state=entry_iter->state;
			}
			else
			if(entry_iter->priority==prev_iter->priority)
			{
				state=entry_iter->state || prev_iter->state;
			}
			else
			if(entry_iter->priority>prev_iter->priority)
			{
				state=entry_iter->state;
			}
			else
			{
				state=prev_iter->state;
			}
		}
	}
	return state;
}




/*void
ValueNode_DynamicList::add(const ValueNode::Handle &value_node, int index)
{
	ListEntry list_entry(value_node);
	list_entry.timing_info.size();

	if(index<0 || index>=(int)list.size())
	{
		list.push_back(list_entry);
	}
	else
	{
		list.insert(list.begin()+index,list_entry);
	}

	add_child(value_node.get());
	reindex();
	//changed();

	if(get_parent_canvas())
		get_parent_canvas()->signal_value_node_child_added()(this,value_node);
	else if(get_root_canvas() && get_parent_canvas())
		get_root_canvas()->signal_value_node_child_added()(this,value_node);
}*/

void
ValueNode_DynamicList::add(const ListEntry &list_entry, int index)
{
	if(index<0 || index>=(int)list.size())
		list.push_back(list_entry);
	else
		list.insert(list.begin()+index,list_entry);
	add_child(list_entry.value_node.get());

	reindex();
	//changed();

	if(get_parent_canvas())
		get_parent_canvas()->signal_value_node_child_added()(this,list_entry.value_node);
	else if(get_root_canvas() && get_parent_canvas())
		get_root_canvas()->signal_value_node_child_added()(this,list_entry.value_node);
}

void
ValueNode_DynamicList::erase(const ValueNode::Handle &value_node_)
{
	ValueNode::Handle value_node(value_node_);

	assert(value_node);
	if(!value_node)
		throw String("ValueNode_DynamicList::erase(): Passed bad value node");

	std::vector<ListEntry>::iterator iter;
	for(iter=list.begin();iter!=list.end();++iter)
		if(iter->value_node==value_node)
		{
			list.erase(iter);
			if(value_node)
			{
				remove_child(value_node.get());
				// changed to fix bug 1420091 - it seems that when a .sif file containing a bline layer encapsulated inside
				// another layer, get_parent_canvas() is false and get_root_canvas() is true, but when we come to erase a
				// vertex, both are true.  So the signal is sent to the parent, but the signal wasn't sent to the parent
				// when it was added.  This probably isn't the right fix, but it seems to work for now.  Note that the same
				// strange "if (X) else if (Y && X)" code is also present in the two previous functions, above.

				// if(get_parent_canvas())
				// 	get_parent_canvas()->signal_value_node_child_removed()(this,value_node);
				// else if(get_root_canvas() && get_parent_canvas())
				//	get_root_canvas()->signal_value_node_child_removed()(this,value_node);
				if(get_non_inline_ancestor_canvas())
					get_non_inline_ancestor_canvas()->invoke_signal_value_node_child_removed(this,value_node);
				else
					printf("%s:%d == can't get non_inline_ancestor_canvas - parent canvas = %lx\n", __FILE__, __LINE__, uintptr_t(get_parent_canvas().get()));
			}
			break;
		}
	reindex();
}


ValueNode_DynamicList::ValueNode_DynamicList(Type &container_type, Canvas::LooseHandle canvas):
	LinkableValueNode(type_list),
	container_type(&container_type),
	loop_(false)
{
	if (getenv("SYNFIG_DEBUG_SET_PARENT_CANVAS"))
		printf("%s:%d set parent canvas for dynamic_list %lx to %lx\n", __FILE__, __LINE__, uintptr_t(this), uintptr_t(canvas.get()));
	set_parent_canvas(canvas);
}

ValueNode_DynamicList::ValueNode_DynamicList(Type &container_type, Type &type, Canvas::LooseHandle canvas):
	LinkableValueNode(type),
	container_type(&container_type),
	loop_(false)
{
	if (getenv("SYNFIG_DEBUG_SET_PARENT_CANVAS"))
		printf("%s:%d set parent canvas for dynamic_list %lx to %lx\n", __FILE__, __LINE__, uintptr_t(this), uintptr_t(canvas.get()));
	set_parent_canvas(canvas);
}


ValueNode_DynamicList::Handle
ValueNode_DynamicList::create_on_canvas(Type &type, Canvas::LooseHandle canvas)
{
	return new ValueNode_DynamicList(type, canvas);
}

ValueNode_DynamicList::~ValueNode_DynamicList()
{
	unlink_all();
}

ValueNode_DynamicList*
ValueNode_DynamicList::create(const ValueBase &value)
{
	//vector<ValueBase> value_list(value.operator vector<ValueBase>());
	vector<ValueBase> value_list(value.get_list());

	vector<ValueBase>::iterator iter;

	if(value_list.empty())
		return 0;

	ValueNode_DynamicList* value_node(new ValueNode_DynamicList(value_list.front().get_type()));

	// when creating a list of vectors, start it off being looped.
	// I think the only time this is used if for creating polygons,
	// and we want them to be looped by default
	if (value_node->get_contained_type() == type_vector)
		value_node->set_loop(true);

	for(iter=value_list.begin();iter!=value_list.end();++iter)
	{
		ValueNode::Handle item(ValueNode_Const::create(*iter));
		value_node->add(ListEntry(item));
		assert(value_node->list.back().value_node);
	}
	return value_node;
}

ValueBase
ValueNode_DynamicList::operator()(Time t)const
{
	if (getenv("SYNFIG_DEBUG_VALUENODE_OPERATORS"))
		printf("%s:%d operator()\n", __FILE__, __LINE__);

	std::vector<ValueBase> ret_list;
	std::vector<ListEntry>::const_iterator iter;

	assert(container_type);

	for(iter=list.begin();iter!=list.end();++iter)
	{
		bool state(iter->status_at_time(t));

		if(state)
		{
			if(iter->value_node->get_type()==*container_type)
				ret_list.push_back((*iter->value_node)(t));
			else
			{
				synfig::warning(string("ValueNode_DynamicList::operator()():")+_("List type/item type mismatch, throwing away mismatch"));
			}
		}
	}

	if(list.empty())
		synfig::warning(string("ValueNode_DynamicList::operator()():")+_("No entries in list"));
	else
	if(ret_list.empty())
		synfig::warning(string("ValueNode_DynamicList::operator()():")+_("No entries in ret_list"));

	return ret_list;
}

bool
ValueNode_DynamicList::set_link_vfunc(int i,ValueNode::Handle x)
{
	assert(i>=0);

	if((unsigned)i>=list.size())
		return false;
	if(x->get_type()!=*container_type)
		return false;
	list[i].value_node=x;
	return true;
}

ValueNode::LooseHandle
ValueNode_DynamicList::get_link_vfunc(int i)const
{
	assert(i>=0);

	if((unsigned)i>=list.size())
		return 0;
	return list[i].value_node;
}

int
ValueNode_DynamicList::link_count()const
{
	return list.size();
}

String
ValueNode_DynamicList::link_local_name(int i)const
{
	assert(i>=0 && i<link_count());

	return etl::strprintf(_("Item %03d"),i+1);
}

ValueNode::Handle
ValueNode_DynamicList::clone(Canvas::LooseHandle canvas, const GUID& deriv_guid)const
{
	{ ValueNode* x(find_value_node(get_guid()^deriv_guid).get()); if(x)return x; }

	ValueNode_DynamicList* ret=dynamic_cast<ValueNode_DynamicList*>(create_new());
	ret->set_guid(get_guid()^deriv_guid);

	std::vector<ListEntry>::const_iterator iter;

	for(iter=list.begin();iter!=list.end();++iter)
	{
		if(iter->value_node->is_exported())
			ret->add(*iter);
		else
		{
			ListEntry list_entry(*iter);
			//list_entry.value_node=find_value_node(iter->value_node->get_guid()^deriv_guid).get();
			//if(!list_entry.value_node)
			list_entry.value_node=iter->value_node->clone(canvas, deriv_guid);
			ret->add(list_entry);
			//ret->list.back().value_node=iter->value_node.clone();
		}
	}
	ret->set_loop(get_loop());
	ret->set_parent_canvas(canvas);
	return ret;
}

String
ValueNode_DynamicList::link_name(int i)const
{
	return strprintf("item%04d",i);
}

int
ValueNode_DynamicList::get_link_index_from_name(const String &name)const
{
	for(int i = 0; i < link_count(); ++i)
		if (link_name(i) == name) return i;
	throw Exception::BadLinkName(name);
}



bool
ValueNode_DynamicList::check_type(Type &type)
{
	return type==type_list;
}

void
ValueNode_DynamicList::set_member_canvas(etl::loose_handle<Canvas> canvas)
{
	for (vector<ListEntry>::iterator iter = list.begin(); iter != list.end(); iter++)
		iter->value_node->set_parent_canvas(canvas);
}

Type&
ValueNode_DynamicList::get_contained_type()const
{
	return *container_type;
}

LinkableValueNode*
ValueNode_DynamicList::create_new()const
{
	return new ValueNode_DynamicList(*container_type);
}

int
ValueNode_DynamicList::find_next_valid_entry(int orig_item, Time t)const
{
	int curr_item;

	for(curr_item=orig_item+1;curr_item!=orig_item;curr_item++)
	{
		if(curr_item==(int)list.size())
		{
			curr_item=0;
			continue;
		}
		if(list[curr_item].status_at_time(t))
			return curr_item;
	}
	return curr_item;
}

int
ValueNode_DynamicList::find_prev_valid_entry(int orig_item, Time t)const
{
	int curr_item;

	for(curr_item=orig_item-1;curr_item!=orig_item;curr_item--)
	{
		if(curr_item==-1)
		{
			curr_item=list.size();
			continue;
		}
		if(list[curr_item].status_at_time(t))
			return curr_item;
	}
	return curr_item;
}

const synfig::Node::time_set	& ValueNode_DynamicList::ListEntry::get_times() const
{
	synfig::ActivepointList::const_iterator 	j = timing_info.begin(),
											end = timing_info.end();

	//must remerge with all the other values because we don't know if we've changed...
	times = value_node->get_times();

	for(; j != end; ++j)
	{
		TimePoint t;
		t.set_time(j->get_time());
		t.set_guid(j->get_guid());

		times.insert(t);
	}

	return times;
}

void ValueNode_DynamicList::get_times_vfunc(Node::time_set &set) const
{
	//add in the active points
	int size = list.size();

	//rebuild all the info...
	for(int i = 0; i < size; ++i)
	{
		const Node::time_set & tset= list[i].get_times();
		set.insert(tset.begin(),tset.end());
	}
}


//new find functions that don't throw
struct timecmp
{
	Time t;

	timecmp(const Time &c) :t(c) {}

	bool operator()(const Activepoint &rhs) const
	{
		return t.is_equal(rhs.get_time());
	}
};

ValueNode_DynamicList::ListEntry::findresult ValueNode_DynamicList::ListEntry::find_uid(const UniqueID& x)
{
	findresult f;
	f.second = false;

	f.first = std::find(timing_info.begin(),timing_info.end(),x);

	if(f.first != timing_info.end())
	{
		f.second = true;
	}

	return f;
}

ValueNode_DynamicList::ListEntry::const_findresult ValueNode_DynamicList::ListEntry::find_uid(const UniqueID& x) const
{
	const_findresult f;
	f.second = false;

	f.first = std::find(timing_info.begin(),timing_info.end(),x);

	if(f.first != timing_info.end())
	{
		f.second = true;
	}

	return f;
}

ValueNode_DynamicList::ListEntry::findresult ValueNode_DynamicList::ListEntry::find_time(const Time& x)
{
	findresult f;
	f.second = false;

	f.first = std::find_if(timing_info.begin(),timing_info.end(),timecmp(x));

	if(f.first != timing_info.end())
	{
		f.second = true;
	}

	return f;
}

ValueNode_DynamicList::ListEntry::const_findresult ValueNode_DynamicList::ListEntry::find_time(const Time& x)const
{
	const_findresult f;
	f.second = false;

	f.first = std::find_if(timing_info.begin(),timing_info.end(),timecmp(x));

	if(f.first != timing_info.end())
	{
		f.second = true;
	}

	return f;
}

void
ValueNode_DynamicList::insert_time(const Time& location, const Time& delta)
{
	if(!delta)
		return;

	std::vector<ListEntry>::iterator iter(list.begin());
	for(;iter!=list.end();++iter)
	{
		try
		{
			ListEntry& item(*iter);

			ActivepointList::iterator iter(item.find_next(location));
			for(;iter!=item.timing_info.end();++iter)
			{
				iter->set_time(iter->get_time()+delta);
			}
		}
		catch(Exception::NotFound&) { }
	}
	changed();
}

LinkableValueNode::Vocab
ValueNode_DynamicList::get_children_vocab_vfunc()const
{
	LinkableValueNode::Vocab ret;
	for(unsigned int i=0; i<list.size();i++)
	{
		ret.push_back(ParamDesc(ValueBase(),strprintf("item%04d",i))
			.set_local_name(etl::strprintf(_("Item %03d"),i+1))
		);
	}

	return ret;
}