Now, Value Desc Link Smart performs the Scale by 1.0 and connect the tangent properly.
[synfig.git] / synfig-studio / src / synfigapp / actions / valuedescsmartlink.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file valuedescsmartlink.cpp
3 **      \brief Template File
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 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "valuedescsmartlink.h"
34
35 #include <synfigapp/canvasinterface.h>
36 #include <synfig/valuenode_const.h>
37 #include <synfig/valuenode_scale.h>
38
39 #include <synfigapp/general.h>
40
41 #endif
42
43 using namespace std;
44 using namespace etl;
45 using namespace synfig;
46 using namespace synfigapp;
47 using namespace Action;
48
49 /* === M A C R O S ========================================================= */
50
51 ACTION_INIT(Action::ValueDescSmartLink);
52 ACTION_SET_NAME(Action::ValueDescSmartLink,"ValueDescSmartLink");
53 ACTION_SET_LOCAL_NAME(Action::ValueDescSmartLink,N_("Smart Link"));
54 ACTION_SET_TASK(Action::ValueDescSmartLink,"connect");
55 ACTION_SET_CATEGORY(Action::ValueDescSmartLink,Action::CATEGORY_VALUEDESC);
56 ACTION_SET_PRIORITY(Action::ValueDescSmartLink,0);
57 ACTION_SET_VERSION(Action::ValueDescSmartLink,"0.0");
58 ACTION_SET_CVS_ID(Action::ValueDescSmartLink,"$Id$");
59
60 /* === G L O B A L S ======================================================= */
61
62 /* === P R O C E D U R E S ================================================= */
63
64 /* === M E T H O D S ======================================================= */
65
66 Action::ValueDescSmartLink::ValueDescSmartLink(): poison(false), status_level(0)
67 {
68 }
69
70 Action::ParamVocab
71 Action::ValueDescSmartLink::get_param_vocab()
72 {
73         ParamVocab ret(Action::CanvasSpecific::get_param_vocab());
74
75         ret.push_back(ParamDesc("value_desc",Param::TYPE_VALUEDESC)
76                 .set_local_name(_("ValueDesc to smart link"))
77                 .set_requires_multiple()
78         );
79
80         return ret;
81 }
82
83 bool
84 Action::ValueDescSmartLink::is_candidate(const ParamList &x)
85 {
86         // If action parameters are not Value Desc
87         if(!candidate_check(get_param_vocab(),x))
88                 return false;
89         // If the size of the List of Param is not two
90
91         // If both value desc aren't Linkable Value Nodes children
92         ParamList::const_iterator iter;
93         int counter=0;
94         Param params[2];
95         for(iter=x.begin(); iter!=x.end(); iter++)
96         {
97                 if(iter->first == "value_desc")
98                 {
99                         params[counter]=iter->second;
100                         counter++;
101                 }
102                 if (counter>2)
103                         return false;
104         }
105         Param pfront(params[0]);
106         Param pback(params[1]);
107         if(!pfront.get_value_desc().parent_is_linkable_value_node() ||
108                 !pback.get_value_desc().parent_is_linkable_value_node())
109                 return false;
110         // If both don't have opposite scalars.
111         if(!((pback.get_value_desc().get_scalar()< 0 && pfront.get_value_desc().get_scalar()> 0)
112                         ||
113                         (pback.get_value_desc().get_scalar()> 0 && pfront.get_value_desc().get_scalar()< 0))
114                 )
115                 return false;
116
117         //We have reached two opposite tangents
118         return true;
119 }
120
121 bool
122 Action::ValueDescSmartLink::set_param(const synfig::String& name, const Action::Param &param)
123 {
124         if(name=="time" && param.get_type()==Param::TYPE_TIME)
125         {
126                 time=param.get_time();
127                 return true;
128         }
129
130         // don't bother looking for the best value to use if there's already been an error
131         if (poison==true) return false;
132
133         if(name=="value_desc" && param.get_type()==Param::TYPE_VALUEDESC)
134         {
135                 ValueDesc value_desc(param.get_value_desc());
136
137                 if(value_desc.is_value_node() && value_desc.get_value_node()->is_exported())
138                 {
139                         if(link_value_node==value_desc.get_value_node())
140                                 return true;
141
142                         if(link_value_node && link_value_node->is_exported())
143                         {
144                                 poison=true;
145                                 status_message = (_("Cannot link two different exported values ('") +
146                                                                   value_desc.get_value_node()->get_id() + _("' and '") +
147                                                                   link_value_node->get_id()) + _("')");
148                                 return false;
149                         }
150
151                         link_value_node=value_desc.get_value_node();
152                         status_message = _("Used exported ValueNode ('") + link_value_node->get_id() + _("').");
153                 }
154                 else if(value_desc.is_value_node())
155                 {
156                         if(!link_value_node)
157                         {
158                                 status_level = 1;
159                                 status_message = _("Using the only available ValueNode.");
160                                 link_value_node=value_desc.get_value_node();
161                         }
162                         else if(link_value_node->is_exported())
163                         {
164                                 // we've already seen an exported value, so use that rather than the current value
165                         }
166                         // Use the one that is referenced more
167                         else if(link_value_node->rcount()!=value_desc.get_value_node()->rcount())
168                         {
169                                 if(link_value_node->rcount()<value_desc.get_value_node()->rcount())
170                                 {
171                                         status_level = 2;
172                                         status_message = _("Using the most referenced ValueNode.");
173                                         link_value_node=value_desc.get_value_node();
174                                 }
175                                 else if (status_level <= 2)
176                                 {
177                                         status_level = 2;
178                                         status_message = _("Using the most referenced ValueNode.");
179                                 }
180                         }
181                         // If the current link value node is a constant and
182                         // this one isn't, then give preference to the exotic
183                         else if(ValueNode_Const::Handle::cast_dynamic(link_value_node) && !ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
184                         {
185                                 status_level = 3;
186                                 status_message = _("There's a tie for most referenced; using the animated ValueNode.");
187                                 link_value_node=value_desc.get_value_node();
188                         }
189                         else if(ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()) && !ValueNode_Const::Handle::cast_dynamic(link_value_node))
190                         {
191                                 if (status_level <= 3)
192                                 {
193                                         status_level = 3;
194                                         status_message = _("There's a tie for most referenced; using the animated ValueNode.");
195                                 }
196                         }
197                         // If both are animated, and this one has more waypoints, then use the one with more waypoints
198                         else if(ValueNode_Animated::Handle::cast_dynamic(link_value_node) &&
199                                         ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()) &&
200                                         ValueNode_Animated::Handle::cast_dynamic(link_value_node)->waypoint_list().size() !=
201                                         ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node())->waypoint_list().size())
202                         {
203                                 if (ValueNode_Animated::Handle::cast_dynamic(link_value_node)->waypoint_list().size() <
204                                         ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node())->waypoint_list().size())
205                                 {
206                                         status_level = 4;
207                                         status_message = _("There's a tie for most referenced, and both are animated; using the one with the most waypoints.");
208                                         link_value_node=value_desc.get_value_node();
209                                 }
210                                 else if (status_level <= 4)
211                                 {
212                                         status_level = 4;
213                                         status_message = _("There's a tie for most referenced, and both are animated; using the one with the most waypoints.");
214                                 }
215                         }
216                         // Use the one that was least recently changed
217                         else if(link_value_node->get_time_last_changed()!=value_desc.get_value_node()->get_time_last_changed())
218                         {
219                                 if(link_value_node->get_time_last_changed()>value_desc.get_value_node()->get_time_last_changed())
220                                 {
221                                         status_level = 5;
222                                         status_message = _("Everything is tied; using the least recently modified value.");
223                                         link_value_node=value_desc.get_value_node();
224                                 }
225                                 else if (status_level <= 5)
226                                 {
227                                         status_level = 5;
228                                         status_message = _("Everything is tied; using the least recently modified value.");
229                                 }
230                         }
231                         else
232                         {
233                                 status_level = 6;
234                                 status_message = _("Absolutely everything is tied.");
235                         }
236                 }
237
238                 if(value_desc_list.size() && value_desc.get_value_type()!=value_desc_list.front().get_value_type())
239                 {
240                         // Everything must be of the same type
241                         poison=true;
242                         status_message = (strprintf(_("Cannot link two values of different types ('%s' and '%s')"),
243                                                                                 ValueBase::type_local_name(value_desc.get_value_type()).c_str(),
244                                                                                 ValueBase::type_local_name(value_desc_list.front().get_value_type()).c_str()));
245                         return false;
246                 }
247
248                 value_desc_list.push_back(value_desc);
249
250                 return true;
251         }
252
253         return Action::CanvasSpecific::set_param(name,param);
254 }
255
256 bool
257 Action::ValueDescSmartLink::is_ready()const
258 {
259         if(poison)
260                 return true;
261         if(value_desc_list.size()!=2)
262                 return false;
263         return Action::CanvasSpecific::is_ready();
264 }
265
266 void
267 Action::ValueDescSmartLink::prepare()
268 {
269         if(poison)
270                 throw Error(status_message.c_str());
271
272         if(value_desc_list.empty())
273                 throw Error(Error::TYPE_NOTREADY);
274
275         clear();
276
277         if(value_desc_list.size()!=2)
278                 throw Error(Error::TYPE_BUG);
279
280         ValueDesc& value_desc_t1(value_desc_list.front());
281         ValueDesc& value_desc_t2(value_desc_list.back());
282         t1 = value_desc_t1.get_value_node();
283         t2 = value_desc_t2.get_value_node();
284
285         if(!link_value_node)
286         {
287                 // we should have a value node selected because is candidate
288                 // should have checked it before
289                 throw Error(Error::TYPE_BUG);
290         }
291
292         //See what is the tangent selected to convert.
293         ValueDesc& toconvert(value_desc_t2);
294                 if(t1==link_value_node)
295                         toconvert=value_desc_t2;        // Convert t2
296                 else if(t2==link_value_node)
297                         toconvert=value_desc_t1;        //Convert t1
298                 else throw Error(Error::TYPE_BUG);
299
300                 //Let's create a Scale Value Node
301                 synfig::ValueNode::Handle scale_value_node=synfig::LinkableValueNode::create("scale",toconvert.get_value(time));
302                 scale_value_node->set_parent_canvas(get_canvas());
303                 //Let's connect the new Scale Value Node
304                 Action::Handle action1(Action::create("ValueDescConnect"));
305                 if(!action1)
306                         throw Error(Error::TYPE_CRITICAL);
307                 action1->set_param("canvas",get_canvas());
308                 action1->set_param("canvas_interface",get_canvas_interface());
309                 action1->set_param("dest",toconvert);
310                 action1->set_param("src",scale_value_node);
311                 assert(action1->is_ready());
312                 if(!action1->is_ready())
313                         throw Error(Error::TYPE_NOTREADY);
314                 add_action_front(action1);
315
316
317                 //Let's Connect the link value node to the scale value node link subparam
318                 Action::Handle action2(Action::create("ValueNodeLinkConnect"));
319                 if(!action2)
320                         throw Error(Error::TYPE_CRITICAL);
321
322                 action2->set_param("canvas",get_canvas());
323                 action2->set_param("canvas_interface",get_canvas_interface());
324                 action2->set_param("parent_value_node",scale_value_node);
325                 action2->set_param("index",0);
326                 action2->set_param("value_node",link_value_node);
327                 assert(action2->is_ready());
328                 if(!action2->is_ready())
329                         throw Error(Error::TYPE_NOTREADY);
330                 add_action_front(action2);
331
332                 //Let's Set the scale to -1
333                 Action::Handle action3(Action::create("ValueNodeConstSet"));
334                 if(!action3)
335                         throw Error(Error::TYPE_CRITICAL);
336
337                 action3->set_param("canvas",get_canvas());
338                 action3->set_param("canvas_interface",get_canvas_interface());
339                 action3->set_param("value_node",synfig::LinkableValueNode::Handle::cast_dynamic(scale_value_node)->get_link(1));
340                 action3->set_param("new_value",synfig::ValueBase(Real(-1.0)));
341                 assert(action3->is_ready());
342                 if(!action3->is_ready())
343                         throw Error(Error::TYPE_NOTREADY);
344                 add_action_front(action3);
345
346         synfig::info("http://synfig.org/Linking#Tier_%d : %s", status_level, status_message.c_str());
347 }