1 /* === S Y N F I G ========================================================= */
2 /*!	\file valuenode_dynamiclist.cpp
3 **	\brief Implementation of the "Dynamic List" valuenode conversion.
4 **
5 **	$Id$
6 **
7 **	\legal
8 **	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **	Copyright (c) 2008 Chris Moore
10 **  Copyright (c) 2011 Carlos López
11 **
12 **	This package is free software; you can redistribute it and/or
13 **	modify it under the terms of the GNU General Public License as
14 **	published by the Free Software Foundation; either version 2 of
15 **	the License, or (at your option) any later version.
16 **
17 **	This package is distributed in the hope that it will be useful,
18 **	but WITHOUT ANY WARRANTY; without even the implied warranty of
19 **	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 **	General Public License for more details.
21 **	\endlegal
22 */
23 /* ========================================================================= */
24 
25 /* === H E A D E R S ======================================================= */
26 
27 #ifdef USING_PCH
28 #	include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #	include <config.h>
32 #endif
33 
34 #include "valuenode_dynamiclist.h"
35 #include "valuenode_const.h"
36 #include "valuenode_composite.h"
37 #include <synfig/general.h>
38 #include <synfig/localization.h>
39 #include <synfig/valuenode_registry.h>
40 #include <synfig/exception.h>
41 #include <vector>
42 #include <list>
43 #include <algorithm>
44 #include <synfig/canvas.h>
45 
46 #endif
47 
48 /* === U S I N G =========================================================== */
49 
50 using namespace std;
51 using namespace etl;
52 using namespace synfig;
53 
54 /* === M A C R O S ========================================================= */
55 
56 /* === G L O B A L S ======================================================= */
57 
58 REGISTER_VALUENODE(ValueNode_DynamicList, RELEASE_VERSION_0_61_06, "dynamic_list", "Dynamic List")
59 
60 /* === P R O C E D U R E S ================================================= */
61 
62 /* === M E T H O D S ======================================================= */
63 
ListEntry()64 ValueNode_DynamicList::ListEntry::ListEntry():
65 	index(0)
66 {
67 }
68 
ListEntry(const ValueNode::Handle & value_node)69 ValueNode_DynamicList::ListEntry::ListEntry(const ValueNode::Handle &value_node):
70 	value_node(value_node),
71 	index(0)
72 {
73 }
74 
ListEntry(const ValueNode::Handle & value_node,Time begin,Time end)75 ValueNode_DynamicList::ListEntry::ListEntry(const ValueNode::Handle &value_node,Time begin, Time end):
76 	value_node(value_node)
77 {
78 	add(begin,false);
79 	add(end,false);
80 	add((begin+end)*0.5,true);
81 }
82 
83 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
add(Time time,bool status,int priority)84 ValueNode_DynamicList::ListEntry::add(Time time, bool status, int priority)
85 {
86 	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList::iterator iterator;
87 
88 	//! \optimize
89 	Activepoint ap(time,status,priority);
90 	ap.set_parent_index(get_index());
91 	ap.set_parent_value_node(get_parent_value_node());
92 	timing_info.push_back(ap);
93 	iterator iter(--iterator(timing_info.end()));
94 	timing_info.sort();
95 
96 	return iter;
97 }
98 
99 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
add(const Activepoint & x)100 ValueNode_DynamicList::ListEntry::add(const Activepoint &x)
101 {
102 	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList::iterator iterator;
103 
104 	//! \optimize
105 	Activepoint ap(x);
106 	ap.set_parent_index(get_index());
107 	ap.set_parent_value_node(get_parent_value_node());
108 	timing_info.push_back(ap);
109 	iterator iter(--iterator(timing_info.end()));
110 	timing_info.sort();
111 
112 	return iter;
113 }
114 
115 void
reindex()116 ValueNode_DynamicList::reindex()
117 {
118 	int i(0);
119 
120 	std::vector<ListEntry>::iterator iter;
121 
122 	for(iter=list.begin();iter!=list.end();++iter)
123 	{
124 		assert(iter->value_node);
125 		if(iter->index!=i || iter->get_parent_value_node().get()!=this)
126 		{
127 			ActivepointList::iterator iter2;
128 
129 			if(iter->timing_info.size()) // is this line really necessary?
130 			for(iter2=iter->timing_info.begin();iter2!=iter->timing_info.end();++iter2)
131 			{
132 				iter2->set_parent_index(i);
133 				iter2->set_parent_value_node(this);
134 			}
135 			iter->index=i;
136 			iter->set_parent_value_node(this);
137 		}
138 	}
139 }
140 
141 ValueNode_DynamicList::ListEntry
create_list_entry(int index,Time time,Real origin)142 ValueNode_DynamicList::create_list_entry(int index, Time time, Real origin)
143 {
144 	ValueNode_DynamicList::ListEntry ret;
145 	int c(link_count());
146 	synfig::ValueBase prev,next;
147 
148 	if(c)
149 		index=index%c;
150 	else
151 		index=0;
152 
153 	assert(index>=0);
154 
155 	ret.index=index;
156 	ret.set_parent_value_node(this);
157 
158 	if(c)
159 	{
160 		next=(*list[index].value_node)(time);
161 
162 		if(index!=0)
163 			prev=(*list[index-1].value_node)(time);
164 		else
165 		{
166 			if(get_loop())
167 			prev=(*list[link_count()-1].value_node)(time);
168 			else
169 			{
170 				prev=next;
171 			}
172 		}
173 	}
174 
175 	Type &type(get_contained_type());
176 	if (type == type_vector)
177 	{
178 		if(c)
179 		{
180 			Vector a(prev.get(Vector())), b(next.get(Vector()));
181 			ret.value_node=ValueNode_Const::create((b-a)*origin+a);
182 		}
183 		else
184 		{
185 			ret.value_node=ValueNode_Const::create(Vector());
186 		}
187 	}
188 	else
189 	if (type == type_real)
190 	{
191 		if(c)
192 		{
193 			Real a(prev.get(Real())), b(next.get(Real()));
194 			ret.value_node=ValueNode_Const::create((b-a)*origin+a);
195 		}
196 		else
197 		{
198 			ret.value_node=ValueNode_Const::create(Real());
199 		}
200 	}
201 	else
202 	if (type == type_color)
203 	{
204 		if(c)
205 		{
206 			Color a(prev.get(Color())), b(next.get(Color()));
207 			ret.value_node=ValueNode_Composite::create((b-a)*origin+a);
208 		}
209 		else
210 		{
211 			ret.value_node=ValueNode_Const::create(Color());
212 		}
213 	}
214 	else
215 	if (type == type_angle)
216 	{
217 		if(c)
218 		{
219 			Angle a(prev.get(Angle())), b(next.get(Angle()));
220 			ret.value_node=ValueNode_Const::create((b-a)*origin+a);
221 		}
222 		else
223 		{
224 			ret.value_node=ValueNode_Const::create(Angle());
225 		}
226 	}
227 	else
228 	if (type == type_time)
229 	{
230 		if(c)
231 		{
232 			Time a(prev.get(Time())), b(next.get(Time()));
233 			ret.value_node=ValueNode_Const::create((b-a)*origin+a);
234 		}
235 		else
236 		{
237 			ret.value_node=ValueNode_Const::create(Time());
238 		}
239 	}
240 	else
241 	{
242 		ret.value_node=ValueNode_Const::create(get_contained_type());
243 	}
244 
245 	ret.value_node->set_parent_canvas(get_parent_canvas());
246 	return ret;
247 }
248 
249 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
find(const UniqueID & x)250 ValueNode_DynamicList::ListEntry::find(const UniqueID& x)
251 {
252 	return std::find(timing_info.begin(),timing_info.end(),x);
253 }
254 
255 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
find(const UniqueID & x) const256 ValueNode_DynamicList::ListEntry::find(const UniqueID& x)const
257 {
258 	return std::find(timing_info.begin(),timing_info.end(),x);
259 }
260 
261 void
erase(const UniqueID & x)262 ValueNode_DynamicList::ListEntry::erase(const UniqueID& x)
263 {
264 	timing_info.erase(find(x));
265 }
266 
267 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
find(const Time & x)268 ValueNode_DynamicList::ListEntry::find(const Time& x)
269 {
270 	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
271 
272 	ActivepointList::iterator iter;
273 
274 	for(iter=timing_info.begin();iter!=timing_info.end();++iter)
275 		if(iter->time==x)
276 			return iter;
277 
278 	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find():"+x.get_string());
279 }
280 
281 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
find(const Time & x) const282 ValueNode_DynamicList::ListEntry::find(const Time& x)const
283 {
284 	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
285 
286 	ActivepointList::const_iterator iter;
287 
288 	for(iter=timing_info.begin();iter!=timing_info.end();++iter)
289 		if(iter->time==x)
290 			return iter;
291 
292 	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find()const:"+x.get_string());
293 }
294 
295 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
find_next(const Time & x)296 ValueNode_DynamicList::ListEntry::find_next(const Time& x)
297 {
298 	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
299 
300 	ActivepointList::iterator iter;
301 
302 	for(iter=timing_info.begin();iter!=timing_info.end();++iter)
303 		if(iter->time>x)
304 			return iter;
305 
306 	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_next():"+x.get_string());
307 }
308 
309 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
find_next(const Time & x) const310 ValueNode_DynamicList::ListEntry::find_next(const Time& x)const
311 {
312 	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
313 
314 	ActivepointList::const_iterator iter;
315 
316 	for(iter=timing_info.begin();iter!=timing_info.end();++iter)
317 		if(iter->time>x)
318 			return iter;
319 
320 	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_next()const:"+x.get_string());
321 }
322 
323 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
find_prev(const Time & x)324 ValueNode_DynamicList::ListEntry::find_prev(const Time& x)
325 {
326 	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
327 
328 	ActivepointList::iterator iter;
329 	iter=timing_info.end();
330 	do
331 	{
332 		--iter;
333 		if(iter->time<x)
334 			return iter;
335 	}
336 	while(iter!=timing_info.begin());
337 
338 	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_prev():"+x.get_string());
339 }
340 
341 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
find_prev(const Time & x) const342 ValueNode_DynamicList::ListEntry::find_prev(const Time& x)const
343 {
344 	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
345 
346 	ActivepointList::const_iterator iter;
347 	iter=timing_info.end();
348 	do
349 	{
350 		--iter;
351 		if(iter->time<x)
352 			return iter;
353 	}
354 	while(iter!=timing_info.begin());
355 
356 	throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_prev()const:"+x.get_string());
357 }
358 
359 int
find(const Time & begin,const Time & end,std::vector<Activepoint * > & selected)360 ValueNode_DynamicList::ListEntry::find(const Time& begin,const Time& end,std::vector<Activepoint*>& selected)
361 {
362 	Time curr_time(begin);
363 	int ret(0);
364 
365 	// try to grab first waypoint
366 	try
367 	{
368 		ActivepointList::iterator iter;
369 		iter=find(curr_time);
370 		selected.push_back(&*iter);
371 		ret++;
372 	}
373 	catch(...) { }
374 
375 	try
376 	{
377 		ActivepointList::iterator iter;
378 		while(true)
379 		{
380 			iter=find_next(curr_time);
381 			curr_time=iter->get_time();
382 			if(curr_time>=end)
383 				break;
384 			selected.push_back(&*iter);
385 			ret++;
386 		}
387 	}
388 	catch(...) { }
389 
390 	return ret;
391 }
392 
393 float
amount_at_time(const Time & t,bool * rising) const394 ValueNode_DynamicList::ListEntry::amount_at_time(const Time &t,bool *rising)const
395 {
396 	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
397 
398 	if(timing_info.empty())
399 		return 1.0f;
400 
401 	try
402 	{
403 		ActivepointList::const_iterator iter;
404 		iter=find(t);
405 		return iter->state?1.0f:0.0f;
406 	}
407 	catch(...) { }
408 
409 	ActivepointList::const_iterator prev_iter;
410 	ActivepointList::const_iterator next_iter;
411 
412 	try	{ prev_iter=find_prev(t); }
413 	catch(...) { return find_next(t)->state?1.0f:0.0f; }
414 
415 	try	{ next_iter=find_next(t); }
416 	catch(...) { return prev_iter->state?1.0f:0.0f; }
417 
418 	if(next_iter->state==prev_iter->state)
419 		return next_iter->state?1.0f:0.0f;
420 
421 	if(rising)*rising=next_iter->state;
422 
423 	if(next_iter->state==true)
424 		return float((t-prev_iter->time)/(next_iter->time-prev_iter->time));
425 
426 	return float((next_iter->time-t)/(next_iter->time-prev_iter->time));
427 }
428 
429 Activepoint
new_activepoint_at_time(const Time & time) const430 ValueNode_DynamicList::ListEntry::new_activepoint_at_time(const Time& time)const
431 {
432 	Activepoint activepoint;
433 
434 	activepoint.set_state(status_at_time(time));
435 	activepoint.set_priority(0);
436 
437 	return activepoint;
438 }
439 
440 bool
status_at_time(const Time & t) const441 ValueNode_DynamicList::ListEntry::status_at_time(const Time &t)const
442 {
443 	//typedef synfig::ValueNode_DynamicList::ListEntry::Activepoint Activepoint;
444 	typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
445 
446 	ActivepointList::const_iterator entry_iter;
447 	ActivepointList::const_iterator prev_iter;
448 	bool state(true);
449 
450 	// New "symmetric" state mechanism
451 	if(!timing_info.empty())
452 	{
453 		if(timing_info.size()==1)
454 			state=timing_info.front().state;
455 		else
456 		{
457 			//! \optimize Perhaps we should use a binary search...?
458 			// This will give us the first activepoint that is after t.
459 			for(entry_iter=timing_info.begin();entry_iter!=timing_info.end();++entry_iter)
460 			{
461 				if(entry_iter->time==t)
462 				{
463 					// If we hit the entry right on the nose, then we don't
464 					// have to do anything more
465 					return entry_iter->state;
466 				}
467 				if(entry_iter->time>t)
468 					break;
469 
470 			}
471 			prev_iter=entry_iter;
472 			prev_iter--;
473 
474 			// ie:
475 			//
476 			//		|-------|---t---|-------|
477 			//	   prev_iter^		^entry_iter
478 
479 			if(entry_iter==timing_info.end())
480 			{
481 				state=prev_iter->state;
482 			}
483 			else
484 			if(entry_iter==timing_info.begin())
485 			{
486 				state=entry_iter->state;
487 			}
488 			else
489 			if(entry_iter->priority==prev_iter->priority)
490 			{
491 				state=entry_iter->state || prev_iter->state;
492 			}
493 			else
494 			if(entry_iter->priority>prev_iter->priority)
495 			{
496 				state=entry_iter->state;
497 			}
498 			else
499 			{
500 				state=prev_iter->state;
501 			}
502 		}
503 	}
504 	return state;
505 }
506 
507 
508 
509 
510 void
add(const ValueNode::Handle & value_node,int index)511 ValueNode_DynamicList::add(const ValueNode::Handle &value_node, int index)
512 {
513 	ListEntry list_entry(value_node);
514 	list_entry.timing_info.size();
515 
516 	if(index<0 || index>=(int)list.size())
517 	{
518 		list.push_back(list_entry);
519 	}
520 	else
521 	{
522 		list.insert(list.begin()+index,list_entry);
523 	}
524 
525 	add_child(value_node.get());
526 	reindex();
527 	//changed();
528 
529 	if(get_parent_canvas())
530 		get_parent_canvas()->signal_value_node_child_added()(this,value_node);
531 	else if(get_root_canvas() && get_parent_canvas())
532 		get_root_canvas()->signal_value_node_child_added()(this,value_node);
533 }
534 
535 void
add(const ListEntry & list_entry,int index)536 ValueNode_DynamicList::add(const ListEntry &list_entry, int index)
537 {
538 	if(index<0 || index>=(int)list.size())
539 		list.push_back(list_entry);
540 	else
541 		list.insert(list.begin()+index,list_entry);
542 	add_child(list_entry.value_node.get());
543 
544 	reindex();
545 	//changed();
546 
547 	if(get_parent_canvas())
548 		get_parent_canvas()->signal_value_node_child_added()(this,list_entry.value_node);
549 	else if(get_root_canvas() && get_parent_canvas())
550 		get_root_canvas()->signal_value_node_child_added()(this,list_entry.value_node);
551 }
552 
553 void
erase(const ValueNode::Handle & value_node_)554 ValueNode_DynamicList::erase(const ValueNode::Handle &value_node_)
555 {
556 	ValueNode::Handle value_node(value_node_);
557 
558 	assert(value_node);
559 	if(!value_node)
560 		throw String("ValueNode_DynamicList::erase(): Passed bad value node");
561 
562 	std::vector<ListEntry>::iterator iter;
563 	for(iter=list.begin();iter!=list.end();++iter)
564 		if(iter->value_node==value_node)
565 		{
566 			list.erase(iter);
567 			if(value_node)
568 			{
569 				remove_child(value_node.get());
570 				// changed to fix bug 1420091 - it seems that when a .sif file containing a bline layer encapsulated inside
571 				// another layer, get_parent_canvas() is false and get_root_canvas() is true, but when we come to erase a
572 				// vertex, both are true.  So the signal is sent to the parent, but the signal wasn't sent to the parent
573 				// when it was added.  This probably isn't the right fix, but it seems to work for now.  Note that the same
574 				// strange "if (X) else if (Y && X)" code is also present in the two previous functions, above.
575 
576 				// if(get_parent_canvas())
577 				// 	get_parent_canvas()->signal_value_node_child_removed()(this,value_node);
578 				// else if(get_root_canvas() && get_parent_canvas())
579 				//	get_root_canvas()->signal_value_node_child_removed()(this,value_node);
580 				if(get_non_inline_ancestor_canvas())
581 					get_non_inline_ancestor_canvas()->invoke_signal_value_node_child_removed(this,value_node);
582 				else
583 					printf("%s:%d == can't get non_inline_ancestor_canvas - parent canvas = %lx\n", __FILE__, __LINE__, uintptr_t(get_parent_canvas().get()));
584 			}
585 			break;
586 		}
587 	reindex();
588 }
589 
590 
ValueNode_DynamicList(Type & container_type,Canvas::LooseHandle canvas)591 ValueNode_DynamicList::ValueNode_DynamicList(Type &container_type, Canvas::LooseHandle canvas):
592 	LinkableValueNode(type_list),
593 	container_type(&container_type),
594 	loop_(false)
595 {
596 	if (getenv("SYNFIG_DEBUG_SET_PARENT_CANVAS"))
597 		printf("%s:%d set parent canvas for dynamic_list %lx to %lx\n", __FILE__, __LINE__, uintptr_t(this), uintptr_t(canvas.get()));
598 	set_parent_canvas(canvas);
599 }
600 
ValueNode_DynamicList(Type & container_type,Type & type,Canvas::LooseHandle canvas)601 ValueNode_DynamicList::ValueNode_DynamicList(Type &container_type, Type &type, Canvas::LooseHandle canvas):
602 	LinkableValueNode(type),
603 	container_type(&container_type),
604 	loop_(false)
605 {
606 	if (getenv("SYNFIG_DEBUG_SET_PARENT_CANVAS"))
607 		printf("%s:%d set parent canvas for dynamic_list %lx to %lx\n", __FILE__, __LINE__, uintptr_t(this), uintptr_t(canvas.get()));
608 	set_parent_canvas(canvas);
609 }
610 
611 
612 ValueNode_DynamicList::Handle
create_on_canvas(Type & type,Canvas::LooseHandle canvas)613 ValueNode_DynamicList::create_on_canvas(Type &type, Canvas::LooseHandle canvas)
614 {
615 	return new ValueNode_DynamicList(type, canvas);
616 }
617 
~ValueNode_DynamicList()618 ValueNode_DynamicList::~ValueNode_DynamicList()
619 {
620 	unlink_all();
621 }
622 
623 ValueNode_DynamicList*
create(const ValueBase & value)624 ValueNode_DynamicList::create(const ValueBase &value)
625 {
626 	//vector<ValueBase> value_list(value.operator vector<ValueBase>());
627 	vector<ValueBase> value_list(value.get_list());
628 
629 	vector<ValueBase>::iterator iter;
630 
631 	if(value_list.empty())
632 		return 0;
633 
634 	ValueNode_DynamicList* value_node(new ValueNode_DynamicList(value_list.front().get_type()));
635 
636 	// when creating a list of vectors, start it off being looped.
637 	// I think the only time this is used if for creating polygons,
638 	// and we want them to be looped by default
639 	if (value_node->get_contained_type() == type_vector)
640 		value_node->set_loop(true);
641 
642 	for(iter=value_list.begin();iter!=value_list.end();++iter)
643 	{
644 		ValueNode::Handle item(ValueNode_Const::create(*iter));
645 		value_node->add(ListEntry(item));
646 		assert(value_node->list.back().value_node);
647 	}
648 	return value_node;
649 }
650 
651 ValueBase
operator ()(Time t) const652 ValueNode_DynamicList::operator()(Time t)const
653 {
654 	if (getenv("SYNFIG_DEBUG_VALUENODE_OPERATORS"))
655 		printf("%s:%d operator()\n", __FILE__, __LINE__);
656 
657 	std::vector<ValueBase> ret_list;
658 	std::vector<ListEntry>::const_iterator iter;
659 
660 	assert(container_type);
661 
662 	for(iter=list.begin();iter!=list.end();++iter)
663 	{
664 		bool state(iter->status_at_time(t));
665 
666 		if(state)
667 		{
668 			if(iter->value_node->get_type()==*container_type)
669 				ret_list.push_back((*iter->value_node)(t));
670 			else
671 			{
672 				synfig::warning(string("ValueNode_DynamicList::operator()():")+_("List type/item type mismatch, throwing away mismatch"));
673 			}
674 		}
675 	}
676 
677 	if(list.empty())
678 		synfig::warning(string("ValueNode_DynamicList::operator()():")+_("No entries in list"));
679 	else
680 	if(ret_list.empty())
681 		synfig::warning(string("ValueNode_DynamicList::operator()():")+_("No entries in ret_list"));
682 
683 	return ret_list;
684 }
685 
686 bool
set_link_vfunc(int i,ValueNode::Handle x)687 ValueNode_DynamicList::set_link_vfunc(int i,ValueNode::Handle x)
688 {
689 	assert(i>=0);
690 
691 	if((unsigned)i>=list.size())
692 		return false;
693 	if(x->get_type()!=*container_type)
694 		return false;
695 	list[i].value_node=x;
696 	return true;
697 }
698 
699 ValueNode::LooseHandle
get_link_vfunc(int i) const700 ValueNode_DynamicList::get_link_vfunc(int i)const
701 {
702 	assert(i>=0);
703 
704 	if((unsigned)i>=list.size())
705 		return 0;
706 	return list[i].value_node;
707 }
708 
709 int
link_count() const710 ValueNode_DynamicList::link_count()const
711 {
712 	return list.size();
713 }
714 
715 String
link_local_name(int i) const716 ValueNode_DynamicList::link_local_name(int i)const
717 {
718 	assert(i>=0 && i<link_count());
719 
720 	return etl::strprintf(_("Item %03d"),i+1);
721 }
722 
723 ValueNode::Handle
clone(Canvas::LooseHandle canvas,const GUID & deriv_guid) const724 ValueNode_DynamicList::clone(Canvas::LooseHandle canvas, const GUID& deriv_guid)const
725 {
726 	{ ValueNode* x(find_value_node(get_guid()^deriv_guid).get()); if(x)return x; }
727 
728 	ValueNode_DynamicList* ret=dynamic_cast<ValueNode_DynamicList*>(create_new());
729 	ret->set_guid(get_guid()^deriv_guid);
730 
731 	std::vector<ListEntry>::const_iterator iter;
732 
733 	for(iter=list.begin();iter!=list.end();++iter)
734 	{
735 		if(iter->value_node->is_exported())
736 			ret->add(*iter);
737 		else
738 		{
739 			ListEntry list_entry(*iter);
740 			//list_entry.value_node=find_value_node(iter->value_node->get_guid()^deriv_guid).get();
741 			//if(!list_entry.value_node)
742 			list_entry.value_node=iter->value_node->clone(canvas, deriv_guid);
743 			ret->add(list_entry);
744 			//ret->list.back().value_node=iter->value_node.clone();
745 		}
746 	}
747 	ret->set_loop(get_loop());
748 	ret->set_parent_canvas(canvas);
749 	return ret;
750 }
751 
752 String
link_name(int i) const753 ValueNode_DynamicList::link_name(int i)const
754 {
755 	return strprintf("item%04d",i);
756 }
757 
758 int
get_link_index_from_name(const String & name) const759 ValueNode_DynamicList::get_link_index_from_name(const String &name)const
760 {
761 	for(int i = 0; i < link_count(); ++i)
762 		if (link_name(i) == name) return i;
763 	throw Exception::BadLinkName(name);
764 }
765 
766 
767 
768 bool
check_type(Type & type)769 ValueNode_DynamicList::check_type(Type &type)
770 {
771 	return type==type_list;
772 }
773 
774 void
set_member_canvas(etl::loose_handle<Canvas> canvas)775 ValueNode_DynamicList::set_member_canvas(etl::loose_handle<Canvas> canvas)
776 {
777 	for (vector<ListEntry>::iterator iter = list.begin(); iter != list.end(); iter++)
778 		iter->value_node->set_parent_canvas(canvas);
779 }
780 
781 Type&
get_contained_type() const782 ValueNode_DynamicList::get_contained_type()const
783 {
784 	return *container_type;
785 }
786 
787 LinkableValueNode*
create_new() const788 ValueNode_DynamicList::create_new()const
789 {
790 	return new ValueNode_DynamicList(*container_type);
791 }
792 
793 int
find_next_valid_entry(int orig_item,Time t) const794 ValueNode_DynamicList::find_next_valid_entry(int orig_item, Time t)const
795 {
796 	int curr_item;
797 
798 	for(curr_item=orig_item+1;curr_item!=orig_item;curr_item++)
799 	{
800 		if(curr_item==(int)list.size())
801 		{
802 			curr_item=0;
803 			continue;
804 		}
805 		if(list[curr_item].status_at_time(t))
806 			return curr_item;
807 	}
808 	return curr_item;
809 }
810 
811 int
find_prev_valid_entry(int orig_item,Time t) const812 ValueNode_DynamicList::find_prev_valid_entry(int orig_item, Time t)const
813 {
814 	int curr_item;
815 
816 	for(curr_item=orig_item-1;curr_item!=orig_item;curr_item--)
817 	{
818 		if(curr_item==-1)
819 		{
820 			curr_item=list.size();
821 			continue;
822 		}
823 		if(list[curr_item].status_at_time(t))
824 			return curr_item;
825 	}
826 	return curr_item;
827 }
828 
get_times() const829 const synfig::Node::time_set	& ValueNode_DynamicList::ListEntry::get_times() const
830 {
831 	synfig::ActivepointList::const_iterator 	j = timing_info.begin(),
832 											end = timing_info.end();
833 
834 	//must remerge with all the other values because we don't know if we've changed...
835 	times = value_node->get_times();
836 
837 	for(; j != end; ++j)
838 	{
839 		TimePoint t;
840 		t.set_time(j->get_time());
841 		t.set_guid(j->get_guid());
842 
843 		times.insert(t);
844 	}
845 
846 	return times;
847 }
848 
get_times_vfunc(Node::time_set & set) const849 void ValueNode_DynamicList::get_times_vfunc(Node::time_set &set) const
850 {
851 	//add in the active points
852 	int size = list.size();
853 
854 	//rebuild all the info...
855 	for(int i = 0; i < size; ++i)
856 	{
857 		const Node::time_set & tset= list[i].get_times();
858 		set.insert(tset.begin(),tset.end());
859 	}
860 }
861 
862 
863 //new find functions that don't throw
864 struct timecmp
865 {
866 	Time t;
867 
timecmptimecmp868 	timecmp(const Time &c) :t(c) {}
869 
operator ()timecmp870 	bool operator()(const Activepoint &rhs) const
871 	{
872 		return t.is_equal(rhs.get_time());
873 	}
874 };
875 
find_uid(const UniqueID & x)876 ValueNode_DynamicList::ListEntry::findresult ValueNode_DynamicList::ListEntry::find_uid(const UniqueID& x)
877 {
878 	findresult f;
879 	f.second = false;
880 
881 	f.first = std::find(timing_info.begin(),timing_info.end(),x);
882 
883 	if(f.first != timing_info.end())
884 	{
885 		f.second = true;
886 	}
887 
888 	return f;
889 }
890 
find_uid(const UniqueID & x) const891 ValueNode_DynamicList::ListEntry::const_findresult ValueNode_DynamicList::ListEntry::find_uid(const UniqueID& x) const
892 {
893 	const_findresult f;
894 	f.second = false;
895 
896 	f.first = std::find(timing_info.begin(),timing_info.end(),x);
897 
898 	if(f.first != timing_info.end())
899 	{
900 		f.second = true;
901 	}
902 
903 	return f;
904 }
905 
find_time(const Time & x)906 ValueNode_DynamicList::ListEntry::findresult ValueNode_DynamicList::ListEntry::find_time(const Time& x)
907 {
908 	findresult f;
909 	f.second = false;
910 
911 	f.first = std::find_if(timing_info.begin(),timing_info.end(),timecmp(x));
912 
913 	if(f.first != timing_info.end())
914 	{
915 		f.second = true;
916 	}
917 
918 	return f;
919 }
920 
find_time(const Time & x) const921 ValueNode_DynamicList::ListEntry::const_findresult ValueNode_DynamicList::ListEntry::find_time(const Time& x)const
922 {
923 	const_findresult f;
924 	f.second = false;
925 
926 	f.first = std::find_if(timing_info.begin(),timing_info.end(),timecmp(x));
927 
928 	if(f.first != timing_info.end())
929 	{
930 		f.second = true;
931 	}
932 
933 	return f;
934 }
935 
936 void
insert_time(const Time & location,const Time & delta)937 ValueNode_DynamicList::insert_time(const Time& location, const Time& delta)
938 {
939 	if(!delta)
940 		return;
941 
942 	std::vector<ListEntry>::iterator iter(list.begin());
943 	for(;iter!=list.end();++iter)
944 	{
945 		try
946 		{
947 			ListEntry& item(*iter);
948 
949 			ActivepointList::iterator iter(item.find_next(location));
950 			for(;iter!=item.timing_info.end();++iter)
951 			{
952 				iter->set_time(iter->get_time()+delta);
953 			}
954 		}
955 		catch(Exception::NotFound) { }
956 	}
957 	changed();
958 }
959 
960 LinkableValueNode::Vocab
get_children_vocab_vfunc() const961 ValueNode_DynamicList::get_children_vocab_vfunc()const
962 {
963 	LinkableValueNode::Vocab ret;
964 	for(unsigned int i=0; i<list.size();i++)
965 	{
966 		ret.push_back(ParamDesc(ValueBase(),strprintf("item%04d",i))
967 			.set_local_name(etl::strprintf(_("Item %03d"),i+1))
968 		);
969 	}
970 
971 	return ret;
972 }
973