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