Merge branch 'master' into genete_core_review
[synfig.git] / synfig-core / src / synfig / valuenode.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file valuenode.cpp
3 **      \brief Implementation of the "Placeholder" valuenode conversion.
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007, 2008 Chris Moore
10 **  Copyright (c) 2008 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.h"
35 #include "general.h"
36 #include "canvas.h"
37 #include "paramdesc.h"
38 #include "releases.h"
39
40 #include "valuenode_const.h"
41 #include "valuenode_linear.h"
42 #include "valuenode_composite.h"
43 #include "valuenode_reference.h"
44 #include "valuenode_greyed.h"
45 #include "valuenode_scale.h"
46 #include "valuenode_blinecalctangent.h"
47 #include "valuenode_blinecalcvertex.h"
48 #include "valuenode_blinecalcwidth.h"
49 #include "valuenode_blinereversetangent.h"
50 #include "valuenode_segcalctangent.h"
51 #include "valuenode_segcalcvertex.h"
52 #include "valuenode_repeat_gradient.h"
53 #include "valuenode_stripes.h"
54 #include "valuenode_range.h"
55 #include "valuenode_add.h"
56 #include "valuenode_subtract.h"
57 #include "valuenode_timedswap.h"
58 #include "valuenode_twotone.h"
59 #include "valuenode_bline.h"
60 #include "valuenode_dynamiclist.h"
61 #include "valuenode_radialcomposite.h"
62 #include "valuenode_gradientrotate.h"
63 #include "valuenode_sine.h"
64 #include "valuenode_cos.h"
65 #include "valuenode_atan2.h"
66 #include "valuenode_exp.h"
67 #include "valuenode_switch.h"
68 #include "valuenode_timeloop.h"
69 #include "valuenode_reciprocal.h"
70 #include "valuenode_duplicate.h"
71 #include "valuenode_integer.h"
72 #include "valuenode_step.h"
73 #include "valuenode_vectorangle.h"
74 #include "valuenode_vectorlength.h"
75 #include "valuenode_vectorx.h"
76 #include "valuenode_vectory.h"
77 #include "valuenode_gradientcolor.h"
78 #include "valuenode_dotproduct.h"
79 #include "valuenode_timestring.h"
80 #include "valuenode_realstring.h"
81 #include "valuenode_join.h"
82 #include "valuenode_anglestring.h"
83 #include "valuenode_intstring.h"
84 #include "valuenode_log.h"
85 #include "valuenode_pow.h"
86 #include "valuenode_compare.h"
87 #include "valuenode_not.h"
88 #include "valuenode_and.h"
89 #include "valuenode_or.h"
90
91 #include "layer.h"
92
93 #endif
94
95 /* === U S I N G =========================================================== */
96
97 using namespace std;
98 using namespace etl;
99 using namespace synfig;
100
101 /* === M A C R O S ========================================================= */
102
103 /* === G L O B A L S ======================================================= */
104
105 static int value_node_count(0);
106
107 static LinkableValueNode::Book *book_;
108
109
110 ValueNode::LooseHandle
111 synfig::find_value_node(const GUID& guid)
112 {
113         return guid_cast<ValueNode>(guid);
114 }
115
116 /* === P R O C E D U R E S ================================================= */
117
118 /* === M E T H O D S ======================================================= */
119
120 bool
121 ValueNode::subsys_init()
122 {
123         book_=new LinkableValueNode::Book();
124
125 #define ADD_VALUENODE(class,name,local,version)                                                                                                 \
126         (*book_)[name].factory=reinterpret_cast<LinkableValueNode::Factory>(&class::create);            \
127         (*book_)[name].check_type=&class::check_type;                                                                                           \
128         (*book_)[name].local_name=local;                                                                                                                        \
129         (*book_)[name].release_version=version
130
131 #define ADD_VALUENODE2(class,name,local,version)                                                                                                \
132         (*book_)[name].factory=reinterpret_cast<LinkableValueNode::Factory>(&class::create_from);       \
133         (*book_)[name].check_type=&class::check_type;                                                                                           \
134         (*book_)[name].local_name=local;                                                                                                                        \
135         (*book_)[name].release_version=version
136
137         ADD_VALUENODE(ValueNode_Linear,                   "linear",                       _("Linear"),                   RELEASE_VERSION_0_61_06);
138         ADD_VALUENODE(ValueNode_Composite,                "composite",            _("Composite"),                RELEASE_VERSION_0_61_06);
139         ADD_VALUENODE(ValueNode_RadialComposite,  "radial_composite", _("Radial Composite"), RELEASE_VERSION_0_61_06);
140         ADD_VALUENODE(ValueNode_Reference,                "reference",            _("Reference"),                RELEASE_VERSION_0_61_06);
141         ADD_VALUENODE(ValueNode_Scale,                    "scale",                        _("Scale"),                    RELEASE_VERSION_0_61_06);
142         ADD_VALUENODE(ValueNode_SegCalcTangent,   "segcalctangent",       _("Segment Tangent"),  RELEASE_VERSION_0_61_06);
143         ADD_VALUENODE(ValueNode_SegCalcVertex,    "segcalcvertex",        _("Segment Vertex"),   RELEASE_VERSION_0_61_06);
144         ADD_VALUENODE(ValueNode_Stripes,                  "stripes",              _("Stripes"),                  RELEASE_VERSION_0_61_06);
145         ADD_VALUENODE(ValueNode_Subtract,                 "subtract",             _("Subtract"),                 RELEASE_VERSION_0_61_06);
146         ADD_VALUENODE(ValueNode_TwoTone,                  "twotone",              _("Two-Tone"),                 RELEASE_VERSION_0_61_06);
147         ADD_VALUENODE(ValueNode_BLine,                    "bline",                        _("BLine"),                    RELEASE_VERSION_0_61_06);
148         ADD_VALUENODE2(ValueNode_DynamicList,     "dynamic_list",         _("Dynamic List"),     RELEASE_VERSION_0_61_06);
149         ADD_VALUENODE(ValueNode_GradientRotate,   "gradient_rotate",  _("Gradient Rotate"),      RELEASE_VERSION_0_61_06);
150         ADD_VALUENODE(ValueNode_Sine,                     "sine",                         _("Sine"),                     RELEASE_VERSION_0_61_06);
151
152         ADD_VALUENODE(ValueNode_TimedSwap,                "timed_swap",           _("Timed Swap"),               RELEASE_VERSION_0_61_07); // SVN r610
153         ADD_VALUENODE(ValueNode_Repeat_Gradient,  "repeat_gradient",  _("Repeat Gradient"),      RELEASE_VERSION_0_61_07); // SVN r666
154         ADD_VALUENODE(ValueNode_Exp,                      "exp",                          _("Exponential"),              RELEASE_VERSION_0_61_07); // SVN r739
155         ADD_VALUENODE(ValueNode_Add,                      "add",                          _("Add"),                              RELEASE_VERSION_0_61_07); // SVN r742
156         ADD_VALUENODE(ValueNode_BLineCalcTangent, "blinecalctangent", _("BLine Tangent"),        RELEASE_VERSION_0_61_07); // SVN r744
157         ADD_VALUENODE(ValueNode_BLineCalcVertex,  "blinecalcvertex",  _("BLine Vertex"),         RELEASE_VERSION_0_61_07); // SVN r744
158         ADD_VALUENODE(ValueNode_Range,                    "range",                        _("Range"),                    RELEASE_VERSION_0_61_07); // SVN r776
159
160         ADD_VALUENODE(ValueNode_Switch,                   "switch",                       _("Switch"),                   RELEASE_VERSION_0_61_08); // SVN r923
161         ADD_VALUENODE(ValueNode_Cos,                      "cos",                          _("Cos"),                              RELEASE_VERSION_0_61_08); // SVN r1111
162         ADD_VALUENODE(ValueNode_Atan2,                    "atan2",                        _("aTan2"),                    RELEASE_VERSION_0_61_08); // SVN r1132
163         ADD_VALUENODE(ValueNode_BLineRevTangent,  "blinerevtangent",  _("Reverse Tangent"),      RELEASE_VERSION_0_61_08); // SVN r1162
164         ADD_VALUENODE(ValueNode_TimeLoop,                 "timeloop",             _("Time Loop"),                RELEASE_VERSION_0_61_08); // SVN r1226
165         ADD_VALUENODE(ValueNode_Reciprocal,               "reciprocal",           _("Reciprocal"),               RELEASE_VERSION_0_61_08); // SVN r1238
166         ADD_VALUENODE(ValueNode_Duplicate,                "duplicate",            _("Duplicate"),                RELEASE_VERSION_0_61_08); // SVN r1267
167         ADD_VALUENODE(ValueNode_Integer,                  "fromint",              _("From Integer"),     RELEASE_VERSION_0_61_08); // SVN r1267
168         ADD_VALUENODE(ValueNode_Step,                     "step",                         _("Step"),                     RELEASE_VERSION_0_61_08); // SVN r1691
169         ADD_VALUENODE(ValueNode_BLineCalcWidth,   "blinecalcwidth",       _("BLine Width"),              RELEASE_VERSION_0_61_08); // SVN r1694
170
171         ADD_VALUENODE(ValueNode_VectorAngle,      "vectorangle",          _("Vector Angle"),     RELEASE_VERSION_0_61_09); // SVN r1880
172         ADD_VALUENODE(ValueNode_VectorLength,     "vectorlength",         _("Vector Length"),    RELEASE_VERSION_0_61_09); // SVN r1881
173         ADD_VALUENODE(ValueNode_VectorX,                  "vectorx",              _("Vector X"),                 RELEASE_VERSION_0_61_09); // SVN r1882
174         ADD_VALUENODE(ValueNode_VectorY,                  "vectory",              _("Vector Y"),                 RELEASE_VERSION_0_61_09); // SVN r1882
175         ADD_VALUENODE(ValueNode_GradientColor,    "gradientcolor",        _("Gradient Color"),   RELEASE_VERSION_0_61_09); // SVN r1885
176         ADD_VALUENODE(ValueNode_DotProduct,               "dotproduct",           _("Dot Product"),              RELEASE_VERSION_0_61_09); // SVN r1891
177         ADD_VALUENODE(ValueNode_TimeString,               "timestring",           _("Time String"),              RELEASE_VERSION_0_61_09); // SVN r2000
178         ADD_VALUENODE(ValueNode_RealString,               "realstring",           _("Real String"),              RELEASE_VERSION_0_61_09); // SVN r2003
179         ADD_VALUENODE(ValueNode_Join,                     "join",                         _("Joined List"),              RELEASE_VERSION_0_61_09); // SVN r2007
180         ADD_VALUENODE(ValueNode_AngleString,      "anglestring",          _("Angle String"),     RELEASE_VERSION_0_61_09); // SVN r2010
181         ADD_VALUENODE(ValueNode_IntString,                "intstring",            _("Int String"),               RELEASE_VERSION_0_61_09); // SVN r2010
182         ADD_VALUENODE(ValueNode_Logarithm,                "logarithm",            _("Logarithm"),                RELEASE_VERSION_0_61_09); // SVN r2034
183
184         ADD_VALUENODE(ValueNode_Greyed,                   "greyed",                       _("Greyed"),                   RELEASE_VERSION_0_62_00); // SVN r2305
185         ADD_VALUENODE(ValueNode_Pow,                  "power",                _("Power"),                    RELEASE_VERSION_0_62_00); // SVN r2362
186         ADD_VALUENODE(ValueNode_Compare,                  "compare",              _("Compare"),                  RELEASE_VERSION_0_62_00); // SVN r2364
187         ADD_VALUENODE(ValueNode_Not,                  "not",                      _("Not"),                              RELEASE_VERSION_0_62_00); // SVN r2364
188         ADD_VALUENODE(ValueNode_And,                  "and",                      _("And"),                              RELEASE_VERSION_0_62_00); // SVN r2364
189         ADD_VALUENODE(ValueNode_Or,                       "or",                   _("Or"),                                       RELEASE_VERSION_0_62_00); // SVN r2364
190
191 #undef ADD_VALUENODE
192 #undef ADD_VALUENODE2
193
194         return true;
195 }
196
197 bool
198 ValueNode::subsys_stop()
199 {
200         delete book_;
201 /*      if(global_value_node_map.size() || value_node_count)
202         {
203                 if(value_node_count)
204                         synfig::error("%d ValueNodes haven't been destroyed yet!",value_node_count);
205
206                 if(global_value_node_map.size()!=value_node_count)
207                         synfig::error("value node count mismatch! map.size()!=value_node_count (%d!=%d)",global_value_node_map.size(),value_node_count);
208
209                 GlobalValueNodeMap::iterator iter;
210                 for(iter=global_value_node_map.begin();iter!=global_value_node_map.end();++iter)
211                 {
212                         if(!iter->second->is_exported())
213                                 synfig::info(_("%s: count:%d name:%s type:%s"),
214                                         iter->first.get_string().c_str(),
215                                         iter->second->count(),
216                                         iter->second->get_name().c_str(),
217                                         ValueBase::type_local_name(iter->second->get_type()).c_str()
218                                 );
219                         else
220                                 synfig::info(_("%s: id:%s count:%d name:%s type:%s"),
221                                         iter->first.get_string().c_str(),
222                                         iter->second->get_id().c_str(),
223                                         iter->second->count(),
224                                         iter->second->get_name().c_str(),
225                                         ValueBase::type_local_name(iter->second->get_type()).c_str()
226                                 );
227                 }
228         }
229 */
230         return true;
231 }
232
233 ValueNode::ValueNode(ValueBase::Type type):type(type)
234 {
235         value_node_count++;
236 }
237
238 LinkableValueNode::Book&
239 LinkableValueNode::book()
240 {
241         return *book_;
242 }
243
244 LinkableValueNode::Handle
245 LinkableValueNode::create(const String &name, const ValueBase& x)
246 {
247         if(!book().count(name))
248                 return 0;
249
250         if (!check_type(name, x.get_type()) &&
251                 // the Duplicate ValueNode is an exception - we don't want the
252                 // user creating it for themselves, so check_type() fails for
253                 // it even when it is valid
254                 !(name == "duplicate" && x.get_type() == ValueBase::TYPE_REAL))
255         {
256                 error(_("Bad type: ValueNode '%s' doesn't accept type '%s'"), book()[name].local_name.c_str(), ValueBase::type_local_name(x.get_type()).c_str());
257                 return 0;
258         }
259
260         return book()[name].factory(x);
261 }
262
263 bool
264 LinkableValueNode::check_type(const String &name, ValueBase::Type x)
265 {
266         if(!book().count(name) || !book()[name].check_type)
267                 return false;
268         return book()[name].check_type(x);
269 }
270
271 bool
272 LinkableValueNode::set_link(int i,ValueNode::Handle x)
273 {
274         ValueNode::Handle previous(get_link(i));
275
276         if(set_link_vfunc(i,x))
277         {
278                 // Fix 2412072: remove the previous link from the parent_set unless one of the other links is also
279                 // using it when we convert a value to 'switch', both 'on' and 'off' are linked to the same valuenode
280                 // if we then disconnect one of the two, the one we disconnect is set to be a new valuenode_const
281                 // and the previously shared value is removed from the parent set even though the other is still
282                 // using it
283                 if(previous)
284                 {
285                         int size = link_count(), index;
286                         for (index=0; index < size; ++index)
287                         {
288                                 if (i == index) continue;
289                                 if (get_link(index) == previous)
290                                         break;
291                         }
292                         if (index == size)
293                                 remove_child(previous.get());
294                 }
295                 add_child(x.get());
296
297                 if(!x->is_exported() && get_parent_canvas())
298                 {
299                         x->set_parent_canvas(get_parent_canvas());
300                 }
301                 changed();
302                 return true;
303         }
304         return false;
305 }
306
307 ValueNode::LooseHandle
308 LinkableValueNode::get_link(int i)const
309 {
310         return get_link_vfunc(i);
311 }
312
313 void
314 LinkableValueNode::unlink_all()
315 {
316         for(int i=0;i<link_count();i++)
317         {
318                 ValueNode::LooseHandle value_node(get_link(i));
319                 if(value_node)
320                         value_node->parent_set.erase(this);
321         }
322 }
323
324 ValueNode::~ValueNode()
325 {
326         value_node_count--;
327
328         begin_delete();
329 }
330
331 void
332 ValueNode::on_changed()
333 {
334         etl::loose_handle<Canvas> parent_canvas = get_parent_canvas();
335         if(parent_canvas)
336                 do                                              // signal to all the ancestor canvases
337                         parent_canvas->signal_value_node_changed()(this);
338                 while (parent_canvas = parent_canvas->parent());
339         else if(get_root_canvas())
340                 get_root_canvas()->signal_value_node_changed()(this);
341
342         Node::on_changed();
343 }
344
345 int
346 ValueNode::replace(etl::handle<ValueNode> x)
347 {
348         if(x.get()==this)
349                 return 0;
350
351         while(parent_set.size())
352         {
353                 (*parent_set.begin())->add_child(x.get());
354                 (*parent_set.begin())->remove_child(this);
355                 //x->parent_set.insert(*parent_set.begin());
356                 //parent_set.erase(parent_set.begin());
357         }
358         int r(RHandle(this).replace(x));
359         x->changed();
360         return r;
361 }
362
363 void
364 ValueNode::set_id(const String &x)
365 {
366         if(name!=x)
367         {
368                 name=x;
369                 signal_id_changed_();
370         }
371 }
372
373 String
374 ValueNode::get_description(bool show_exported_name)const
375 {
376         String ret(_("ValueNode"));
377
378         if (dynamic_cast<const LinkableValueNode*>(this))
379                 return (dynamic_cast<const LinkableValueNode*>(this))->get_description(-1, show_exported_name);
380
381         if (show_exported_name && !is_exported())
382                 show_exported_name = false;
383
384         if (show_exported_name)
385                 ret += strprintf(" (%s)", get_id().c_str());
386
387         return ret;
388 }
389
390 ValueNodeList::ValueNodeList():
391         placeholder_count_(0)
392 {
393 }
394
395 bool
396 ValueNodeList::count(const String &id)const
397 {
398         const_iterator iter;
399
400         if(id.empty())
401                 return false;
402
403         for(iter=begin();iter!=end() && id!=(*iter)->get_id();++iter)
404                 ;
405
406         if(iter==end())
407                 return false;
408
409         return true;
410 }
411
412 ValueNode::Handle
413 ValueNodeList::find(const String &id)
414 {
415         iterator iter;
416
417         if(id.empty())
418                 throw Exception::IDNotFound("Empty ID");
419
420         for(iter=begin();iter!=end() && id!=(*iter)->get_id();++iter)
421                 ;
422
423         if(iter==end())
424                 throw Exception::IDNotFound("ValueNode in ValueNodeList: "+id);
425
426         return *iter;
427 }
428
429 ValueNode::ConstHandle
430 ValueNodeList::find(const String &id)const
431 {
432         const_iterator iter;
433
434         if(id.empty())
435                 throw Exception::IDNotFound("Empty ID");
436
437         for(iter=begin();iter!=end() && id!=(*iter)->get_id();++iter)
438                 ;
439
440         if(iter==end())
441                 throw Exception::IDNotFound("ValueNode in ValueNodeList: "+id);
442
443         return *iter;
444 }
445
446 ValueNode::Handle
447 ValueNodeList::surefind(const String &id)
448 {
449         if(id.empty())
450                 throw Exception::IDNotFound("Empty ID");
451
452         ValueNode::Handle value_node;
453
454         try
455         {
456                 value_node=find(id);
457         }
458         catch(Exception::IDNotFound)
459         {
460                 value_node=PlaceholderValueNode::create();
461                 value_node->set_id(id);
462                 push_back(value_node);
463                 placeholder_count_++;
464         }
465
466         return value_node;
467 }
468
469 bool
470 ValueNodeList::erase(ValueNode::Handle value_node)
471 {
472         assert(value_node);
473
474         iterator iter;
475
476         for(iter=begin();iter!=end();++iter)
477                 if(value_node.get()==iter->get())
478                 {
479                         std::list<ValueNode::RHandle>::erase(iter);
480                         if(PlaceholderValueNode::Handle::cast_dynamic(value_node))
481                                 placeholder_count_--;
482                         return true;
483                 }
484         return false;
485 }
486
487 bool
488 ValueNodeList::add(ValueNode::Handle value_node)
489 {
490         if(!value_node)
491                 return false;
492         if(value_node->get_id().empty())
493                 return false;
494
495         try
496         {
497                 ValueNode::RHandle other_value_node=find(value_node->get_id());
498                 if(PlaceholderValueNode::Handle::cast_dynamic(other_value_node))
499                 {
500                         other_value_node->replace(value_node);
501                         placeholder_count_--;
502                         return true;
503                 }
504
505                 return false;
506         }
507         catch(Exception::IDNotFound)
508         {
509                 push_back(value_node);
510                 return true;
511         }
512
513         return false;
514 }
515
516 void
517 ValueNodeList::audit()
518 {
519         iterator iter,next;
520
521         for(next=begin(),iter=next++;iter!=end();iter=next++)
522                 if(iter->count()==1)
523                         std::list<ValueNode::RHandle>::erase(iter);
524 }
525
526
527 String
528 PlaceholderValueNode::get_name()const
529 {
530         return "placeholder";
531 }
532
533 String
534 PlaceholderValueNode::get_local_name()const
535 {
536         return _("Placeholder");
537 }
538
539 ValueNode*
540 PlaceholderValueNode::clone(const GUID& deriv_guid)const
541 {
542         ValueNode* ret(new PlaceholderValueNode());
543         ret->set_guid(get_guid()^deriv_guid);
544         return ret;
545 }
546
547 PlaceholderValueNode::Handle
548 PlaceholderValueNode::create(ValueBase::Type type)
549 {
550         return new PlaceholderValueNode(type);
551 }
552
553 ValueBase
554 PlaceholderValueNode::operator()(Time /*t*/)const
555 {
556         assert(0);
557         return ValueBase();
558 }
559
560 PlaceholderValueNode::PlaceholderValueNode(ValueBase::Type type):
561         ValueNode(type)
562 {
563 }
564
565 ValueNode*
566 LinkableValueNode::clone(const GUID& deriv_guid)const
567 {
568         {
569                 ValueNode* x(find_value_node(get_guid()^deriv_guid).get());
570                 if(x)
571                         return x;
572         }
573
574         int i;
575         LinkableValueNode *ret=create_new();
576         ret->set_guid(get_guid()^deriv_guid);
577
578         for(i=0;i<link_count();i++)
579         {
580                 ValueNode::Handle link=get_link_vfunc(i);
581                 if(!link->is_exported())
582                 {
583                         ValueNode::LooseHandle value_node(find_value_node(link->get_guid()^deriv_guid));
584                         if(!value_node)
585                                 value_node=link->clone(deriv_guid);
586                         ret->set_link(i,value_node);
587                 }
588                 else
589                         ret->set_link(i,link);
590         }
591
592         return ret;
593 }
594
595 String
596 ValueNode::get_relative_id(etl::loose_handle<const Canvas> x)const
597 {
598         assert(is_exported());
599         assert(canvas_);
600
601         if(x.get()==canvas_.get())
602                 return get_id();
603
604         return canvas_->_get_relative_id(x)+':'+get_id();
605 }
606
607 void
608 ValueNode::set_parent_canvas(etl::loose_handle<Canvas> x)
609 {
610         canvas_=x; if(x) root_canvas_=x->get_root();
611 }
612
613 void
614 ValueNode::set_root_canvas(etl::loose_handle<Canvas> x)
615 {
616         root_canvas_=x->get_root();
617 }
618
619 void LinkableValueNode::get_times_vfunc(Node::time_set &set) const
620 {
621         ValueNode::LooseHandle  h;
622
623         int size = link_count();
624
625         //just add it to the set...
626         for(int i=0; i < size; ++i)
627         {
628                 h = get_link(i);
629
630                 if(h)
631                 {
632                         const Node::time_set &tset = h->get_times();
633                         set.insert(tset.begin(),tset.end());
634                 }
635         }
636 }
637
638 String
639 LinkableValueNode::get_description(int index, bool show_exported_name)const
640 {
641         String description;
642
643         if (index == -1)
644         {
645                 if (show_exported_name && is_exported())
646                         description += strprintf(" (%s)", get_id().c_str());
647         }
648         else
649         {
650                 description = String(":") + link_local_name(index);
651
652                 if (show_exported_name)
653                 {
654                         ValueNode::LooseHandle link(get_link(index));
655                         if (link->is_exported())
656                                 description += strprintf(" (%s)", link->get_id().c_str());
657                 }
658         }
659
660         const synfig::Node* node = this;
661         LinkableValueNode::ConstHandle parent_linkable_vn = 0;
662
663         // walk up through the valuenodes trying to find the layer at the top
664         while (!node->parent_set.empty() && !dynamic_cast<const Layer*>(node))
665         {
666                 LinkableValueNode::ConstHandle linkable_value_node(dynamic_cast<const LinkableValueNode*>(node));
667                 if (linkable_value_node)
668                 {
669                         String link;
670                         int cnt = linkable_value_node->link_count();
671                         for (int i = 0; i < cnt; i++)
672                                 if (linkable_value_node->get_link(i) == parent_linkable_vn)
673                                 {
674                                         link = String(":") + linkable_value_node->link_local_name(i);
675                                         break;
676                                 }
677
678                         description = linkable_value_node->get_local_name() + link + (parent_linkable_vn?">":"") + description;
679                 }
680                 node = *node->parent_set.begin();
681                 parent_linkable_vn = linkable_value_node;
682         }
683
684         Layer::ConstHandle parent_layer(dynamic_cast<const Layer*>(node));
685         if(parent_layer)
686         {
687                 String param;
688                 const Layer::DynamicParamList &dynamic_param_list(parent_layer->dynamic_param_list());
689                 // loop to find the parameter in the dynamic parameter list - this gives us its name
690                 for (Layer::DynamicParamList::const_iterator iter = dynamic_param_list.begin(); iter != dynamic_param_list.end(); iter++)
691                         if (iter->second == parent_linkable_vn)
692                                 param = String(":") + parent_layer->get_param_local_name(iter->first);
693                 description = strprintf("(%s)%s>%s",
694                                                                 parent_layer->get_non_empty_description().c_str(),
695                                                                 param.c_str(),
696                                                                 description.c_str());
697         }
698
699         return description;
700 }