Fixed the way that the decision is made which value to use when linking values togeth...
[synfig.git] / synfig-studio / trunk / src / synfigapp / actions / valuedesclink.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file valuedesclink.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include "valuedesclink.h"
33
34 #include <synfigapp/canvasinterface.h>
35 #include <synfig/valuenode_const.h>
36
37 #endif
38
39 using namespace std;
40 using namespace etl;
41 using namespace synfig;
42 using namespace synfigapp;
43 using namespace Action;
44
45 /* === M A C R O S ========================================================= */
46
47 ACTION_INIT(Action::ValueDescLink);
48 ACTION_SET_NAME(Action::ValueDescLink,"value_desc_link");
49 ACTION_SET_LOCAL_NAME(Action::ValueDescLink,"Link");
50 ACTION_SET_TASK(Action::ValueDescLink,"connect");
51 ACTION_SET_CATEGORY(Action::ValueDescLink,Action::CATEGORY_VALUEDESC);
52 ACTION_SET_PRIORITY(Action::ValueDescLink,0);
53 ACTION_SET_VERSION(Action::ValueDescLink,"0.0");
54 ACTION_SET_CVS_ID(Action::ValueDescLink,"$Id$");
55
56 /* === G L O B A L S ======================================================= */
57
58 /* === P R O C E D U R E S ================================================= */
59
60 /* === M E T H O D S ======================================================= */
61
62 Action::ValueDescLink::ValueDescLink(): poison(false), status_level(0)
63 {
64 }
65
66 Action::ParamVocab
67 Action::ValueDescLink::get_param_vocab()
68 {
69         ParamVocab ret(Action::CanvasSpecific::get_param_vocab());
70
71         ret.push_back(ParamDesc("value_desc",Param::TYPE_VALUEDESC)
72                 .set_local_name(_("ValueDesc to link"))
73                 .set_requires_multiple()
74         );
75
76         return ret;
77 }
78
79 bool
80 Action::ValueDescLink::is_candidate(const ParamList &x)
81 {
82         return candidate_check(get_param_vocab(),x);
83 }
84
85 bool
86 Action::ValueDescLink::set_param(const synfig::String& name, const Action::Param &param)
87 {
88         if(name=="time" && param.get_type()==Param::TYPE_TIME)
89         {
90                 time=param.get_time();
91                 return true;
92         }
93
94         // don't bother looking for the best value to use if there's already been an error
95         if (poison==true) return false;
96
97         if(name=="value_desc" && param.get_type()==Param::TYPE_VALUEDESC)
98         {
99                 ValueDesc value_desc(param.get_value_desc());
100
101                 if(value_desc.is_value_node() && value_desc.get_value_node()->is_exported())
102                 {
103                         if(link_value_node==value_desc.get_value_node())
104                                 return true;
105
106                         if(link_value_node && link_value_node->is_exported())
107                         {
108                                 poison=true;
109                                 status_message = (_("Cannot link two different exported values ('") +
110                                                                   value_desc.get_value_node()->get_id() + _("' and '") +
111                                                                   link_value_node->get_id()) + _("')");
112                                 return false;
113                         }
114
115                         link_value_node=value_desc.get_value_node();
116                         status_message = _("Used exported ValueNode ('") + link_value_node->get_id() + _("').");
117                 }
118                 else if(value_desc.is_value_node())
119                 {
120                         if(!link_value_node)
121                         {
122                                 status_level = 1;
123                                 status_message = _("Using the only available ValueNode.");
124                                 link_value_node=value_desc.get_value_node();
125                         }
126                         else if(link_value_node->is_exported())
127                         {
128                                 // we've already seen an exported value, so use that rather than the current value
129                         }
130                         // Use the one that is referenced more
131                         else if(link_value_node->rcount()!=value_desc.get_value_node()->rcount())
132                         {
133                                 if(link_value_node->rcount()<value_desc.get_value_node()->rcount())
134                                 {
135                                         status_level = 2;
136                                         status_message = _("Using the most referenced ValueNode.");
137                                         link_value_node=value_desc.get_value_node();
138                                 }
139                                 else if (status_level <= 2)
140                                 {
141                                         status_level = 2;
142                                         status_message = _("Using the most referenced ValueNode.");
143                                 }
144                         }
145                         // If the current link value node is a constant and
146                         // this one isn't, then give preference to the exotic
147                         else if(ValueNode_Const::Handle::cast_dynamic(link_value_node) && !ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
148                         {
149                                 status_level = 3;
150                                 status_message = _("There's a tie for most referenced; using the animated ValueNode.");
151                                 link_value_node=value_desc.get_value_node();
152                         }
153                         else if(ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()) && !ValueNode_Const::Handle::cast_dynamic(link_value_node))
154                         {
155                                 if (status_level <= 3)
156                                 {
157                                         status_level = 3;
158                                         status_message = _("There's a tie for most referenced; using the animated ValueNode.");
159                                 }
160                         }
161                         // If both are animated, and this one has more waypoints, then use the one with more waypoints
162                         else if(ValueNode_Animated::Handle::cast_dynamic(link_value_node) &&
163                                         ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()) &&
164                                         ValueNode_Animated::Handle::cast_dynamic(link_value_node)->waypoint_list().size() !=
165                                         ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node())->waypoint_list().size())
166                         {
167                                 if (ValueNode_Animated::Handle::cast_dynamic(link_value_node)->waypoint_list().size() <
168                                         ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node())->waypoint_list().size())
169                                 {
170                                         status_level = 4;
171                                         status_message = _("There's a tie for most referenced, and both are animated; using the one with the most waypoints.");
172                                         link_value_node=value_desc.get_value_node();
173                                 }
174                                 else if (status_level <= 4)
175                                 {
176                                         status_level = 4;
177                                         status_message = _("There's a tie for most referenced, and both are animated; using the one with the most waypoints.");
178                                 }
179                         }
180                         // Use the one that was least recently changed
181                         else if(link_value_node->get_time_last_changed()!=value_desc.get_value_node()->get_time_last_changed())
182                         {
183                                 if(link_value_node->get_time_last_changed()>value_desc.get_value_node()->get_time_last_changed())
184                                 {
185                                         status_level = 5;
186                                         status_message = _("Everything is tied; using the least recently modified value.");
187                                         link_value_node=value_desc.get_value_node();
188                                 }
189                                 else if (status_level <= 5)
190                                 {
191                                         status_level = 5;
192                                         status_message = _("Everything is tied; using the least recently modified value.");
193                                 }
194                         }
195                         else
196                         {
197                                 status_level = 6;
198                                 status_message = _("Absolutely everything is tied.");
199                         }
200                 }
201
202                 if(value_desc_list.size() && value_desc.get_value_type()!=value_desc_list.front().get_value_type())
203                 {
204                         // Everything must be of the same type
205                         poison=true;
206                         status_message = (_("Cannot link two values of different types ('") +
207                                                           value_desc.get_value().type_name() + _("' and '") +
208                                                           value_desc_list.front().get_value().type_name() +_("')"));
209                         return false;
210                 }
211
212                 value_desc_list.push_back(value_desc);
213
214                 return true;
215         }
216
217         return Action::CanvasSpecific::set_param(name,param);
218 }
219
220 bool
221 Action::ValueDescLink::is_ready()const
222 {
223         if(poison)
224                 return true;
225         if(value_desc_list.size()<=1)
226                 return false;
227         return Action::CanvasSpecific::is_ready();
228 }
229
230 void
231 Action::ValueDescLink::prepare()
232 {
233         if(poison)
234                 throw Error(status_message.c_str());
235
236         if(value_desc_list.empty())
237                 throw Error(Error::TYPE_NOTREADY);
238
239         clear();
240
241         if(!link_value_node)
242         {
243                 status_message = _("No ValueNodes were available, so one was created.");
244                 ValueDesc& value_desc(value_desc_list.front());
245
246                 link_value_node=ValueNode_Const::create(value_desc.get_value(time));
247
248                 Action::Handle action(Action::create("value_desc_connect"));
249
250                 action->set_param("canvas",get_canvas());
251                 action->set_param("canvas_interface",get_canvas_interface());
252                 action->set_param("src",link_value_node);
253                 action->set_param("dest",value_desc);
254
255                 assert(action->is_ready());
256                 if(!action->is_ready())
257                         throw Error(Error::TYPE_NOTREADY);
258
259                 add_action_front(action);
260         }
261
262         /*
263         if(!link_value_node->is_exported())
264         {
265                 Action::Handle action(Action::create("value_node_add"));
266
267                 action->set_param("canvas",get_canvas());
268                 action->set_param("canvas_interface",get_canvas_interface());
269                 action->set_param("new",link_value_node);
270                 action->set_param("name",strprintf(_("Unnamed%08d"),synfig::UniqueID().get_uid()));
271
272                 assert(action->is_ready());
273                 if(!action->is_ready())
274                         throw Error(Error::TYPE_NOTREADY);
275
276                 add_action_front(action);
277         }
278         */
279
280         std::list<ValueDesc>::iterator iter;
281         for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
282         {
283                 ValueDesc& value_desc(*iter);
284
285                 Action::Handle action(Action::create("value_desc_connect"));
286
287                 action->set_param("canvas",get_canvas());
288                 action->set_param("canvas_interface",get_canvas_interface());
289                 action->set_param("src",link_value_node);
290                 action->set_param("dest",value_desc);
291
292                 assert(action->is_ready());
293                 if(!action->is_ready())
294                         throw Error(Error::TYPE_NOTREADY);
295
296                 add_action_front(action);
297         }
298
299         synfig::info(status_message);
300 }