Remove .gitignore do nothing is ignored.
[synfig.git] / synfig-core / trunk / src / synfig / valuenode.cpp
index 5358530..d1e3256 100644 (file)
@@ -1,27 +1,30 @@
-/* === S I N F G =========================================================== */
-/*!    \file value_node.cpp
-**     \brief Template File
+/* === S Y N F I G ========================================================= */
+/*!    \file valuenode.cpp
+**     \brief Implementation of the "Placeholder" valuenode conversion.
 **
-**     $Id: valuenode.cpp,v 1.1.1.1 2005/01/04 01:23:15 darco Exp $
+**     $Id$
 **
 **     \legal
-**     Copyright (c) 2002 Robert B. Quattlebaum Jr.
+**     Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
+**     Copyright (c) 2007, 2008 Chris Moore
+**  Copyright (c) 2008 Carlos López
 **
-**     This software and associated documentation
-**     are CONFIDENTIAL and PROPRIETARY property of
-**     the above-mentioned copyright holder.
+**     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.
 **
-**     You may not copy, print, publish, or in any
-**     other way distribute this software without
-**     a prior written agreement with
-**     the copyright holder.
+**     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 ======================================================= */
 
-#define SINFG_NO_ANGLE
+#define SYNFIG_NO_ANGLE
 
 //#define HAS_HASH_MAP 1
 
 #include "valuenode.h"
 #include "general.h"
 #include "canvas.h"
+#include "paramdesc.h"
+#include "releases.h"
 
 #include "valuenode_const.h"
 #include "valuenode_linear.h"
 #include "valuenode_composite.h"
 #include "valuenode_reference.h"
+#include "valuenode_greyed.h"
 #include "valuenode_scale.h"
+#include "valuenode_blinecalctangent.h"
+#include "valuenode_blinecalcvertex.h"
+#include "valuenode_blinecalcwidth.h"
+#include "valuenode_blinereversetangent.h"
 #include "valuenode_segcalctangent.h"
 #include "valuenode_segcalcvertex.h"
+#include "valuenode_repeat_gradient.h"
 #include "valuenode_stripes.h"
+#include "valuenode_range.h"
+#include "valuenode_add.h"
 #include "valuenode_subtract.h"
 #include "valuenode_timedswap.h"
 #include "valuenode_twotone.h"
 #include "valuenode_radialcomposite.h"
 #include "valuenode_gradientrotate.h"
 #include "valuenode_sine.h"
+#include "valuenode_cos.h"
+#include "valuenode_atan2.h"
+#include "valuenode_exp.h"
+#include "valuenode_switch.h"
+#include "valuenode_timeloop.h"
+#include "valuenode_reciprocal.h"
+#include "valuenode_duplicate.h"
+#include "valuenode_integer.h"
+#include "valuenode_step.h"
+#include "valuenode_vectorangle.h"
+#include "valuenode_vectorlength.h"
+#include "valuenode_vectorx.h"
+#include "valuenode_vectory.h"
+#include "valuenode_gradientcolor.h"
+#include "valuenode_dotproduct.h"
+#include "valuenode_timestring.h"
+#include "valuenode_realstring.h"
+#include "valuenode_join.h"
+#include "valuenode_anglestring.h"
+#include "valuenode_intstring.h"
+#include "valuenode_log.h"
+#include "valuenode_pow.h"
+#include "valuenode_compare.h"
+#include "valuenode_not.h"
+#include "valuenode_and.h"
+#include "valuenode_or.h"
 
 #include "layer.h"
 
 
 using namespace std;
 using namespace etl;
-using namespace sinfg;
+using namespace synfig;
 
 /* === M A C R O S ========================================================= */
 
@@ -73,7 +112,7 @@ static LinkableValueNode::Book *book_;
 
 
 ValueNode::LooseHandle
-sinfg::find_value_node(const GUID& guid)
+synfig::find_value_node(const GUID& guid)
 {
        return guid_cast<ValueNode>(guid);
 }
@@ -87,26 +126,75 @@ ValueNode::subsys_init()
 {
        book_=new LinkableValueNode::Book();
 
-#define ADD_VALUENODE(c,n) (*book_)[n].factory=reinterpret_cast<LinkableValueNode::Factory>(&c::create); (*book_)[n].check_type=&c::check_type;(*book_)[n].local_name=n
-#define ADD_VALUENODE2(c,n) (*book_)[n].factory=reinterpret_cast<LinkableValueNode::Factory>(&c::create_from); (*book_)[n].check_type=&c::check_type;(*book_)[n].local_name=n
-
-       ADD_VALUENODE(ValueNode_Linear,"linear");
-       ADD_VALUENODE(ValueNode_Composite,"composite");
-       ADD_VALUENODE(ValueNode_RadialComposite,"radial_composite");
-       ADD_VALUENODE(ValueNode_Reference,"reference");
-       ADD_VALUENODE(ValueNode_Scale,"scale");
-       ADD_VALUENODE(ValueNode_SegCalcTangent,"segcalctangent");
-       ADD_VALUENODE(ValueNode_SegCalcVertex,"segcalcvertex");
-       ADD_VALUENODE(ValueNode_Stripes,"stripes");
-       ADD_VALUENODE(ValueNode_Subtract,"subtract");
-       //ADD_VALUENODE(ValueNode_TimedSwap,"timed_swap");
-       ADD_VALUENODE(ValueNode_TwoTone,"twotone");
-       ADD_VALUENODE(ValueNode_BLine,"bline");
-       ADD_VALUENODE2(ValueNode_DynamicList,"dynamic_list");
-       ADD_VALUENODE(ValueNode_GradientRotate,"gradient_rotate");
-       ADD_VALUENODE(ValueNode_Sine,"sine");
-       
+#define ADD_VALUENODE(class,name,local,version)                                                                                                        \
+       (*book_)[name].factory=reinterpret_cast<LinkableValueNode::Factory>(&class::create);            \
+       (*book_)[name].check_type=&class::check_type;                                                                                           \
+       (*book_)[name].local_name=local;                                                                                                                        \
+       (*book_)[name].release_version=version
+
+#define ADD_VALUENODE2(class,name,local,version)                                                                                               \
+       (*book_)[name].factory=reinterpret_cast<LinkableValueNode::Factory>(&class::create_from);       \
+       (*book_)[name].check_type=&class::check_type;                                                                                           \
+       (*book_)[name].local_name=local;                                                                                                                        \
+       (*book_)[name].release_version=version
+
+       ADD_VALUENODE(ValueNode_Linear,                   "linear",                       _("Linear"),                   RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_Composite,                "composite",            _("Composite"),                RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_RadialComposite,  "radial_composite", _("Radial Composite"), RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_Reference,                "reference",            _("Reference"),                RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_Scale,                    "scale",                        _("Scale"),                    RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_SegCalcTangent,   "segcalctangent",       _("Segment Tangent"),  RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_SegCalcVertex,    "segcalcvertex",        _("Segment Vertex"),   RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_Stripes,                  "stripes",              _("Stripes"),                  RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_Subtract,                 "subtract",             _("Subtract"),                 RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_TwoTone,                  "twotone",              _("Two-Tone"),                 RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_BLine,                    "bline",                        _("BLine"),                    RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE2(ValueNode_DynamicList,     "dynamic_list",         _("Dynamic List"),     RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_GradientRotate,   "gradient_rotate",  _("Gradient Rotate"),      RELEASE_VERSION_0_61_06);
+       ADD_VALUENODE(ValueNode_Sine,                     "sine",                         _("Sine"),                     RELEASE_VERSION_0_61_06);
+
+       ADD_VALUENODE(ValueNode_TimedSwap,                "timed_swap",           _("Timed Swap"),               RELEASE_VERSION_0_61_07); // SVN r610
+       ADD_VALUENODE(ValueNode_Repeat_Gradient,  "repeat_gradient",  _("Repeat Gradient"),      RELEASE_VERSION_0_61_07); // SVN r666
+       ADD_VALUENODE(ValueNode_Exp,                      "exp",                          _("Exponential"),              RELEASE_VERSION_0_61_07); // SVN r739
+       ADD_VALUENODE(ValueNode_Add,                      "add",                          _("Add"),                              RELEASE_VERSION_0_61_07); // SVN r742
+       ADD_VALUENODE(ValueNode_BLineCalcTangent, "blinecalctangent", _("BLine Tangent"),        RELEASE_VERSION_0_61_07); // SVN r744
+       ADD_VALUENODE(ValueNode_BLineCalcVertex,  "blinecalcvertex",  _("BLine Vertex"),         RELEASE_VERSION_0_61_07); // SVN r744
+       ADD_VALUENODE(ValueNode_Range,                    "range",                        _("Range"),                    RELEASE_VERSION_0_61_07); // SVN r776
+
+       ADD_VALUENODE(ValueNode_Switch,                   "switch",                       _("Switch"),                   RELEASE_VERSION_0_61_08); // SVN r923
+       ADD_VALUENODE(ValueNode_Cos,                      "cos",                          _("Cos"),                              RELEASE_VERSION_0_61_08); // SVN r1111
+       ADD_VALUENODE(ValueNode_Atan2,                    "atan2",                        _("aTan2"),                    RELEASE_VERSION_0_61_08); // SVN r1132
+       ADD_VALUENODE(ValueNode_BLineRevTangent,  "blinerevtangent",  _("Reverse Tangent"),      RELEASE_VERSION_0_61_08); // SVN r1162
+       ADD_VALUENODE(ValueNode_TimeLoop,                 "timeloop",             _("Time Loop"),                RELEASE_VERSION_0_61_08); // SVN r1226
+       ADD_VALUENODE(ValueNode_Reciprocal,               "reciprocal",           _("Reciprocal"),               RELEASE_VERSION_0_61_08); // SVN r1238
+       ADD_VALUENODE(ValueNode_Duplicate,                "duplicate",            _("Duplicate"),                RELEASE_VERSION_0_61_08); // SVN r1267
+       ADD_VALUENODE(ValueNode_Integer,                  "fromint",              _("From Integer"),     RELEASE_VERSION_0_61_08); // SVN r1267
+       ADD_VALUENODE(ValueNode_Step,                     "step",                         _("Step"),                     RELEASE_VERSION_0_61_08); // SVN r1691
+       ADD_VALUENODE(ValueNode_BLineCalcWidth,   "blinecalcwidth",       _("BLine Width"),              RELEASE_VERSION_0_61_08); // SVN r1694
+
+       ADD_VALUENODE(ValueNode_VectorAngle,      "vectorangle",          _("Vector Angle"),     RELEASE_VERSION_0_61_09); // SVN r1880
+       ADD_VALUENODE(ValueNode_VectorLength,     "vectorlength",         _("Vector Length"),    RELEASE_VERSION_0_61_09); // SVN r1881
+       ADD_VALUENODE(ValueNode_VectorX,                  "vectorx",              _("Vector X"),                 RELEASE_VERSION_0_61_09); // SVN r1882
+       ADD_VALUENODE(ValueNode_VectorY,                  "vectory",              _("Vector Y"),                 RELEASE_VERSION_0_61_09); // SVN r1882
+       ADD_VALUENODE(ValueNode_GradientColor,    "gradientcolor",        _("Gradient Color"),   RELEASE_VERSION_0_61_09); // SVN r1885
+       ADD_VALUENODE(ValueNode_DotProduct,               "dotproduct",           _("Dot Product"),              RELEASE_VERSION_0_61_09); // SVN r1891
+       ADD_VALUENODE(ValueNode_TimeString,               "timestring",           _("Time String"),              RELEASE_VERSION_0_61_09); // SVN r2000
+       ADD_VALUENODE(ValueNode_RealString,               "realstring",           _("Real String"),              RELEASE_VERSION_0_61_09); // SVN r2003
+       ADD_VALUENODE(ValueNode_Join,                     "join",                         _("Joined List"),              RELEASE_VERSION_0_61_09); // SVN r2007
+       ADD_VALUENODE(ValueNode_AngleString,      "anglestring",          _("Angle String"),     RELEASE_VERSION_0_61_09); // SVN r2010
+       ADD_VALUENODE(ValueNode_IntString,                "intstring",            _("Int String"),               RELEASE_VERSION_0_61_09); // SVN r2010
+       ADD_VALUENODE(ValueNode_Logarithm,                "logarithm",            _("Logarithm"),                RELEASE_VERSION_0_61_09); // SVN r2034
+
+       ADD_VALUENODE(ValueNode_Greyed,                   "greyed",                       _("Greyed"),                   RELEASE_VERSION_0_61_10); // SVN r2305
+       ADD_VALUENODE(ValueNode_Pow,                  "power",                _("Power"),                    RELEASE_VERSION_0_61_10); // SVN r2362
+       ADD_VALUENODE(ValueNode_Compare,                  "compare",              _("Compare"),                  RELEASE_VERSION_0_61_10); // SVN r2364
+       ADD_VALUENODE(ValueNode_Not,                  "not",                      _("Not"),                              RELEASE_VERSION_0_61_10); // SVN r2364
+       ADD_VALUENODE(ValueNode_And,                  "and",                      _("And"),                              RELEASE_VERSION_0_61_10); // SVN r2364
+       ADD_VALUENODE(ValueNode_Or,                       "or",                   _("Or"),                                       RELEASE_VERSION_0_61_10); // SVN r2364
+
 #undef ADD_VALUENODE
+#undef ADD_VALUENODE2
+
        return true;
 }
 
@@ -117,28 +205,28 @@ ValueNode::subsys_stop()
 /*     if(global_value_node_map.size() || value_node_count)
        {
                if(value_node_count)
-                       sinfg::error("%d ValueNodes haven't been destroyed yet!",value_node_count);
+                       synfig::error("%d ValueNodes haven't been destroyed yet!",value_node_count);
 
                if(global_value_node_map.size()!=value_node_count)
-                       sinfg::error("value node count mismatch! map.size()!=value_node_count (%d!=%d)",global_value_node_map.size(),value_node_count);
-               
-               GlobalValueNodeMap::iterator iter;              
+                       synfig::error("value node count mismatch! map.size()!=value_node_count (%d!=%d)",global_value_node_map.size(),value_node_count);
+
+               GlobalValueNodeMap::iterator iter;
                for(iter=global_value_node_map.begin();iter!=global_value_node_map.end();++iter)
                {
                        if(!iter->second->is_exported())
-                               sinfg::info("%s: count:%d name:%s type:%s",
+                               synfig::info(_("%s: count:%d name:%s type:%s"),
                                        iter->first.get_string().c_str(),
                                        iter->second->count(),
                                        iter->second->get_name().c_str(),
-                                       ValueBase::type_name(iter->second->get_type()).c_str()
+                                       ValueBase::type_local_name(iter->second->get_type()).c_str()
                                );
                        else
-                               sinfg::info("%s: id:%s count:%d name:%s type:%s",
+                               synfig::info(_("%s: id:%s count:%d name:%s type:%s"),
                                        iter->first.get_string().c_str(),
                                        iter->second->get_id().c_str(),
                                        iter->second->count(),
                                        iter->second->get_name().c_str(),
-                                       ValueBase::type_name(iter->second->get_type()).c_str()
+                                       ValueBase::type_local_name(iter->second->get_type()).c_str()
                                );
                }
        }
@@ -162,6 +250,17 @@ LinkableValueNode::create(const String &name, const ValueBase& x)
 {
        if(!book().count(name))
                return 0;
+
+       if (!check_type(name, x.get_type()) &&
+               // the Duplicate ValueNode is an exception - we don't want the
+               // user creating it for themselves, so check_type() fails for
+               // it even when it is valid
+               !(name == "duplicate" && x.get_type() == ValueBase::TYPE_REAL))
+       {
+               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());
+               return 0;
+       }
+
        return book()[name].factory(x);
 }
 
@@ -177,13 +276,28 @@ bool
 LinkableValueNode::set_link(int i,ValueNode::Handle x)
 {
        ValueNode::Handle previous(get_link(i));
-       
+
        if(set_link_vfunc(i,x))
        {
+               // Fix 2412072: remove the previous link from the parent_set unless one of the other links is also
+               // using it when we convert a value to 'switch', both 'on' and 'off' are linked to the same valuenode
+               // if we then disconnect one of the two, the one we disconnect is set to be a new valuenode_const
+               // and the previously shared value is removed from the parent set even though the other is still
+               // using it
                if(previous)
-                       remove_child(previous.get());
+               {
+                       int size = link_count(), index;
+                       for (index=0; index < size; ++index)
+                       {
+                               if (i == index) continue;
+                               if (get_link(index) == previous)
+                                       break;
+                       }
+                       if (index == size)
+                               remove_child(previous.get());
+               }
                add_child(x.get());
-               
+
                if(!x->is_exported() && get_parent_canvas())
                {
                        x->set_parent_canvas(get_parent_canvas());
@@ -214,20 +328,21 @@ LinkableValueNode::unlink_all()
 ValueNode::~ValueNode()
 {
        value_node_count--;
-       
-       begin_delete();
 
-       //DEBUGPOINT();
+       begin_delete();
 }
 
 void
 ValueNode::on_changed()
 {
-       if(get_parent_canvas())
-               get_parent_canvas()->signal_value_node_changed()(this);
-       else if(get_root_canvas() && get_parent_canvas())
+       etl::loose_handle<Canvas> parent_canvas = get_parent_canvas();
+       if(parent_canvas)
+               do                                              // signal to all the ancestor canvases
+                       parent_canvas->signal_value_node_changed()(this);
+               while (parent_canvas = parent_canvas->parent());
+       else if(get_root_canvas())
                get_root_canvas()->signal_value_node_changed()(this);
-       
+
        Node::on_changed();
 }
 
@@ -236,7 +351,7 @@ ValueNode::replace(etl::handle<ValueNode> x)
 {
        if(x.get()==this)
                return 0;
-       
+
        while(parent_set.size())
        {
                (*parent_set.begin())->add_child(x.get());
@@ -259,6 +374,23 @@ ValueNode::set_id(const String &x)
        }
 }
 
+String
+ValueNode::get_description(bool show_exported_name)const
+{
+       String ret(_("ValueNode"));
+
+       if (dynamic_cast<const LinkableValueNode*>(this))
+               return (dynamic_cast<const LinkableValueNode*>(this))->get_description(-1, show_exported_name);
+
+       if (show_exported_name && !is_exported())
+               show_exported_name = false;
+
+       if (show_exported_name)
+               ret += strprintf(" (%s)", get_id().c_str());
+
+       return ret;
+}
+
 ValueNodeList::ValueNodeList():
        placeholder_count_(0)
 {
@@ -272,7 +404,8 @@ ValueNodeList::count(const String &id)const
        if(id.empty())
                return false;
 
-       for(iter=begin();iter!=end() && id!=(*iter)->get_id();++iter);
+       for(iter=begin();iter!=end() && id!=(*iter)->get_id();++iter)
+               ;
 
        if(iter==end())
                return false;
@@ -288,7 +421,8 @@ ValueNodeList::find(const String &id)
        if(id.empty())
                throw Exception::IDNotFound("Empty ID");
 
-       for(iter=begin();iter!=end() && id!=(*iter)->get_id();++iter);
+       for(iter=begin();iter!=end() && id!=(*iter)->get_id();++iter)
+               ;
 
        if(iter==end())
                throw Exception::IDNotFound("ValueNode in ValueNodeList: "+id);
@@ -304,7 +438,8 @@ ValueNodeList::find(const String &id)const
        if(id.empty())
                throw Exception::IDNotFound("Empty ID");
 
-       for(iter=begin();iter!=end() && id!=(*iter)->get_id();++iter);
+       for(iter=begin();iter!=end() && id!=(*iter)->get_id();++iter)
+               ;
 
        if(iter==end())
                throw Exception::IDNotFound("ValueNode in ValueNodeList: "+id);
@@ -317,9 +452,9 @@ ValueNodeList::surefind(const String &id)
 {
        if(id.empty())
                throw Exception::IDNotFound("Empty ID");
-       
+
        ValueNode::Handle value_node;
-       
+
        try
        {
                value_node=find(id);
@@ -374,11 +509,11 @@ ValueNodeList::add(ValueNode::Handle value_node)
                return false;
        }
        catch(Exception::IDNotFound)
-       {       
+       {
                push_back(value_node);
                return true;
        }
-       
+
        return false;
 }
 
@@ -420,7 +555,7 @@ PlaceholderValueNode::create(ValueBase::Type type)
 }
 
 ValueBase
-PlaceholderValueNode::operator()(Time t)const
+PlaceholderValueNode::operator()(Time /*t*/)const
 {
        assert(0);
        return ValueBase();
@@ -434,12 +569,16 @@ PlaceholderValueNode::PlaceholderValueNode(ValueBase::Type type):
 ValueNode*
 LinkableValueNode::clone(const GUID& deriv_guid)const
 {
-       { ValueNode* x(find_value_node(get_guid()^deriv_guid).get()); if(x)return x; }
+       {
+               ValueNode* x(find_value_node(get_guid()^deriv_guid).get());
+               if(x)
+                       return x;
+       }
 
        int i;
        LinkableValueNode *ret=create_new();
        ret->set_guid(get_guid()^deriv_guid);
-       
+
        for(i=0;i<link_count();i++)
        {
                ValueNode::Handle link=get_link_vfunc(i);
@@ -462,10 +601,10 @@ ValueNode::get_relative_id(etl::loose_handle<const Canvas> x)const
 {
        assert(is_exported());
        assert(canvas_);
-       
+
        if(x.get()==canvas_.get())
                return get_id();
-       
+
        return canvas_->_get_relative_id(x)+':'+get_id();
 }
 
@@ -484,9 +623,9 @@ ValueNode::set_root_canvas(etl::loose_handle<Canvas> x)
 void LinkableValueNode::get_times_vfunc(Node::time_set &set) const
 {
        ValueNode::LooseHandle  h;
-       
+
        int size = link_count();
-               
+
        //just add it to the set...
        for(int i=0; i < size; ++i)
        {
@@ -499,3 +638,67 @@ void LinkableValueNode::get_times_vfunc(Node::time_set &set) const
                }
        }
 }
+
+String
+LinkableValueNode::get_description(int index, bool show_exported_name)const
+{
+       String description;
+
+       if (index == -1)
+       {
+               if (show_exported_name && is_exported())
+                       description += strprintf(" (%s)", get_id().c_str());
+       }
+       else
+       {
+               description = String(":") + link_local_name(index);
+
+               if (show_exported_name)
+               {
+                       ValueNode::LooseHandle link(get_link(index));
+                       if (link->is_exported())
+                               description += strprintf(" (%s)", link->get_id().c_str());
+               }
+       }
+
+       const synfig::Node* node = this;
+       LinkableValueNode::ConstHandle parent_linkable_vn = 0;
+
+       // walk up through the valuenodes trying to find the layer at the top
+       while (!node->parent_set.empty() && !dynamic_cast<const Layer*>(node))
+       {
+               LinkableValueNode::ConstHandle linkable_value_node(dynamic_cast<const LinkableValueNode*>(node));
+               if (linkable_value_node)
+               {
+                       String link;
+                       int cnt = linkable_value_node->link_count();
+                       for (int i = 0; i < cnt; i++)
+                               if (linkable_value_node->get_link(i) == parent_linkable_vn)
+                               {
+                                       link = String(":") + linkable_value_node->link_local_name(i);
+                                       break;
+                               }
+
+                       description = linkable_value_node->get_local_name() + link + (parent_linkable_vn?">":"") + description;
+               }
+               node = *node->parent_set.begin();
+               parent_linkable_vn = linkable_value_node;
+       }
+
+       Layer::ConstHandle parent_layer(dynamic_cast<const Layer*>(node));
+       if(parent_layer)
+       {
+               String param;
+               const Layer::DynamicParamList &dynamic_param_list(parent_layer->dynamic_param_list());
+               // loop to find the parameter in the dynamic parameter list - this gives us its name
+               for (Layer::DynamicParamList::const_iterator iter = dynamic_param_list.begin(); iter != dynamic_param_list.end(); iter++)
+                       if (iter->second == parent_linkable_vn)
+                               param = String(":") + parent_layer->get_param_local_name(iter->first);
+               description = strprintf("(%s)%s>%s",
+                                                               parent_layer->get_non_empty_description().c_str(),
+                                                               param.c_str(),
+                                                               description.c_str());
+       }
+
+       return description;
+}