The value desc smart link now does the scale conversion.
[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         //Study the particular case of two tangents of a bline being linked.
293         ValueDesc& toconvert(value_desc_t2);
294                 if(t1==link_value_node)
295                 {
296                         // Convert t2
297                         synfig::info("converting t2");
298                         toconvert=value_desc_t2;
299                 }
300                 else if(t2==link_value_node)
301                 {
302                         //Convert t1
303                         synfig::info("converting t1");
304                         toconvert=value_desc_t1;
305                 }
306                 else { synfig::info("error"); }
307
308                 Action::Handle action(Action::create("ValueDescConvert"));
309                 action->set_param("canvas",get_canvas());
310                 action->set_param("canvas_interface",get_canvas_interface());
311                 action->set_param("time",time);
312                 action->set_param("type","scale");
313                 action->set_param("value_desc",toconvert);
314                 assert(action->is_ready());
315                 if(!action->is_ready())
316                         throw Error(Error::TYPE_NOTREADY);
317                 add_action_front(action);
318
319         synfig::info("http://synfig.org/Linking#Tier_%d : %s", status_level, status_message.c_str());
320 }