1e715c3e7fd5b62d4dcb37d23bcdad665b50ebb2
[synfig.git] / synfig-core / src / synfig / loadcanvas.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file loadcanvas.cpp
3 **      \brief writeme
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) 2009 Carlos A. Sosa Navarro
11 **      Copyright (c) 2009 Nikita Kitaev
12 **
13 **      This package is free software; you can redistribute it and/or
14 **      modify it under the terms of the GNU General Public License as
15 **      published by the Free Software Foundation; either version 2 of
16 **      the License, or (at your option) any later version.
17 **
18 **      This package is distributed in the hope that it will be useful,
19 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 **      General Public License for more details.
22 **      \endlegal
23 */
24 /* ========================================================================= */
25
26 /* === H E A D E R S ======================================================= */
27
28 #ifdef USING_PCH
29 #       include "pch.h"
30 #else
31 #ifdef HAVE_CONFIG_H
32 #       include <config.h>
33 #endif
34
35 #include <cstdlib>
36 #include <cstdio>
37 #include <cstring>
38 #include <ETL/stringf>
39 #include <libxml++/libxml++.h>
40 #include <vector>
41 #include <stdexcept>
42 #include <iostream>
43
44 #include "layer_pastecanvas.h"
45 #include "loadcanvas.h"
46 #include "valuenode.h"
47 #include "valuenode_animated.h"
48 #include "valuenode_composite.h"
49 #include "valuenode_const.h"
50 #include "valuenode_linear.h"
51 #include "valuenode_dynamiclist.h"
52 #include "valuenode_reference.h"
53 #include "valuenode_scale.h"
54 #include "valuenode_timedswap.h"
55 #include "valuenode_twotone.h"
56 #include "valuenode_stripes.h"
57 #include "valuenode_segcalctangent.h"
58 #include "valuenode_segcalcvertex.h"
59 #include "valuenode_bline.h"
60
61 #include "layer.h"
62 #include "string.h"
63
64 #include "exception.h"
65
66 #include "gradient.h"
67
68 #include <map>
69 #include <sigc++/bind.h>
70
71 #endif
72
73 /* === U S I N G =========================================================== */
74
75 using namespace std;
76 using namespace synfig;
77 using namespace etl;
78
79 /*
80 class test_class {
81 static int bleh;
82 public:
83         test_class() { assert(!bleh); bleh++; synfig::info("test_class: initi: %d",bleh); }
84         ~test_class() { assert(bleh); synfig::info("test_class: uninit: %d",bleh); bleh--; }
85 };
86 int test_class::bleh(0);
87
88 test_class test_class_instance;
89 */
90
91 /* === M A C R O S ========================================================= */
92
93 #define VALUENODE_COMPATIBILITY_URL "http://synfig.org/Convert#Compatibility"
94
95 inline bool is_whitespace(char x) { return ((x)=='\n' || (x)=='\t' || (x)==' '); }
96
97 std::set<String> CanvasParser::loading_;
98
99 /* === P R O C E D U R E S ================================================= */
100
101 static std::map<String, Canvas::LooseHandle>* open_canvas_map_(0);
102
103 std::map<synfig::String, etl::loose_handle<Canvas> >& synfig::get_open_canvas_map()
104 {
105         if(!open_canvas_map_)
106                 open_canvas_map_=new std::map<String, Canvas::LooseHandle>;
107         return *open_canvas_map_;
108 }
109
110 static void _remove_from_open_canvas_map(Canvas *x) { get_open_canvas_map().erase(etl::absolute_path(x->get_file_name())); }
111
112 static void _canvas_file_name_changed(Canvas *x)
113 {
114         std::map<synfig::String, etl::loose_handle<Canvas> >::iterator iter;
115
116         for(iter=get_open_canvas_map().begin();iter!=get_open_canvas_map().end();++iter)
117                 if(iter->second==x)
118                         break;
119         assert(iter!=get_open_canvas_map().end());
120         if(iter==get_open_canvas_map().end())
121                 return;
122         get_open_canvas_map().erase(iter->first);
123         get_open_canvas_map()[etl::absolute_path(x->get_file_name())]=x;
124
125 }
126
127 Canvas::Handle
128 synfig::open_canvas(const String &filename,String &errors,String &warnings)
129 {
130         return open_canvas_as(filename, filename, errors, warnings);
131 }
132
133 Canvas::Handle
134 synfig::open_canvas_as(const String &filename,const String &as,String &errors,String &warnings)
135 {
136         if (CanvasParser::loading_.count(filename))
137         {
138                 String warning(strprintf(_("cannot load '%s' recursively"), filename.c_str()));
139                 synfig::warning(warning);
140                 warnings = "  * " + warning + "\n";
141                 Canvas::Handle canvas(Canvas::create());
142                 canvas->set_file_name(filename);
143                 Layer::Handle paste(Layer_PasteCanvas::create());
144                 canvas->push_back(paste);
145                 paste->set_description(warning);
146                 return canvas;
147         }
148
149         Canvas::Handle canvas;
150         CanvasParser parser;
151         parser.set_allow_errors(true);
152
153         try
154         {
155                 CanvasParser::loading_.insert(filename);
156                 canvas=parser.parse_from_file_as(filename,as,errors);
157         }
158         catch (...)
159         {
160                 CanvasParser::loading_.erase(filename);
161                 throw;
162         }
163         CanvasParser::loading_.erase(filename);
164
165         warnings = parser.get_warnings_text();
166
167         if(parser.error_count())
168         {
169                 errors = parser.get_errors_text();
170                 return Canvas::Handle();
171         }
172
173         return canvas;
174 }
175
176 /* === M E T H O D S ======================================================= */
177
178 void
179 CanvasParser::error_unexpected_element(xmlpp::Node *element,const String &got, const String &expected)
180 {
181         error(element,strprintf(_("Unexpected element <%s>, Expected <%s>"),got.c_str(),expected.c_str()));
182 }
183
184 void
185 CanvasParser::error_unexpected_element(xmlpp::Node *element,const String &got)
186 {
187         error(element,strprintf(_("Unexpected element <%s>"),got.c_str()));
188 }
189
190 void
191 CanvasParser::warning(xmlpp::Node *element, const String &text)
192 {
193         string str=strprintf("%s:<%s>:%d: ",filename.c_str(),element->get_name().c_str(),element->get_line())+text;
194
195         synfig::warning(str);
196         // cerr<<str<<endl;
197
198         total_warnings_++;
199         warnings_text += "  * " + str + "\n";
200         if(total_warnings_>=max_warnings_)
201                 fatal_error(element, _("Too many warnings"));
202 }
203
204 void
205 CanvasParser::error(xmlpp::Node *element, const String &text)
206 {
207         string str=strprintf("%s:<%s>:%d: error: ",filename.c_str(),element->get_name().c_str(),element->get_line())+text;
208         total_errors_++;
209         errors_text += "  * " + str + "\n";
210         if(!allow_errors_)
211                 throw runtime_error(str);
212         cerr<<str<<endl;
213         //      synfig::error(str);
214 }
215
216 void
217 CanvasParser::fatal_error(xmlpp::Node *element, const String &text)
218 {
219         string str=strprintf("%s:<%s>:%d:",filename.c_str(),element->get_name().c_str(),element->get_line())+text;
220         throw runtime_error(str);
221 }
222
223
224
225 Keyframe
226 CanvasParser::parse_keyframe(xmlpp::Element *element,Canvas::Handle canvas)
227 {
228         assert(element->get_name()=="keyframe");
229
230         if(!element->get_attribute("time"))
231         {
232                 error(element,strprintf(_("<%s> is missing \"%s\" attribute"),"real","time"));
233                 return Keyframe();
234         }
235
236         Keyframe ret(Time(element->get_attribute("time")->get_value(),canvas->rend_desc().get_frame_rate()));
237
238
239         if(element->get_children().empty())
240                 return ret;
241
242         if(element->get_child_text()->get_content().empty())
243                 return ret;
244
245         ret.set_description(element->get_child_text()->get_content());
246
247         return ret;
248 }
249
250
251 Real
252 CanvasParser::parse_real(xmlpp::Element *element)
253 {
254         assert(element->get_name()=="real");
255
256         if(!element->get_children().empty())
257                 warning(element, strprintf(_("<%s> should not contain anything"),"real"));
258
259         if(!element->get_attribute("value"))
260         {
261                 error(element,strprintf(_("<%s> is missing \"value\" attribute"),"real"));
262                 return false;
263         }
264
265         string val=element->get_attribute("value")->get_value();
266
267         return atof(val.c_str());
268 }
269
270 Time
271 CanvasParser::parse_time(xmlpp::Element *element,Canvas::Handle canvas)
272 {
273         assert(element->get_name()=="time");
274
275         if(!element->get_children().empty())
276                 warning(element, strprintf(_("<%s> should not contain anything"),"time"));
277
278         if(!element->get_attribute("value"))
279         {
280                 error(element,strprintf(_("<%s> is missing \"value\" attribute"),"time"));
281                 return false;
282         }
283
284         string val=element->get_attribute("value")->get_value();
285
286         return Time(val,canvas->rend_desc().get_frame_rate());
287 }
288
289 int
290 CanvasParser::parse_integer(xmlpp::Element *element)
291 {
292         assert(element->get_name()=="integer");
293
294         if(!element->get_children().empty())
295                 warning(element, strprintf(_("<%s> should not contain anything"),"integer"));
296
297         if(!element->get_attribute("value"))
298         {
299                 error(element,strprintf(_("<%s> is missing \"value\" attribute"),"integer"));
300                 return false;
301         }
302
303         string val=element->get_attribute("value")->get_value();
304
305         return atoi(val.c_str());
306 }
307
308 // see 'minor hack' at the end of parse_vector() below
309 // making this 'static' to give it file local scope
310 // stops it working (where working means working around
311 // bug #1509627)
312 Vector &canvas_parser_vector_id(Vector &vector)
313 {
314         return vector;
315 }
316
317 Vector
318 CanvasParser::parse_vector(xmlpp::Element *element)
319 {
320         assert(element->get_name()=="vector");
321
322         if(element->get_children().empty())
323         {
324                 error(element, "Undefined value in <vector>");
325                 return Vector();
326         }
327
328         Vector vect;
329
330         xmlpp::Element::NodeList list = element->get_children();
331         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
332         {
333                 xmlpp::Element *child=dynamic_cast<xmlpp::Element*>((xmlpp::Node*)*iter);
334                 if(!child)
335                         continue;
336                 else
337                 if(child->get_name()=="x")
338                 {
339                         if(child->get_children().empty())
340                         {
341                                 error(element, "Undefined value in <x>");
342                                 return Vector();
343                         }
344                         vect[0]=atof(child->get_child_text()->get_content().c_str());
345                 }
346                 else
347                 if(child->get_name()=="y")
348                 {
349                         if(child->get_children().empty())
350                         {
351                                 error(element, "Undefined value in <y>");
352                                 return Vector();
353                         }
354                         vect[1]=atof(child->get_child_text()->get_content().c_str());
355                 }
356                 else
357                         error_unexpected_element(child,child->get_name());
358         }
359         // Minor hack - gcc 4.1.2 and earlier think that we're not using
360         // 'vect' and optimize it out at -O2 and higher.  This convinces
361         // them that we are really using it.
362         return canvas_parser_vector_id(vect);
363         // When the bug is fixed, we can just do this instead:
364         // return vect;
365 }
366
367 Color
368 CanvasParser::parse_color(xmlpp::Element *element)
369 {
370         assert(element->get_name()=="color");
371
372         if(element->get_children().empty())
373         {
374                 error(element, "Undefined value in <color>");
375                 return Color();
376         }
377
378         Color color(0);
379
380         xmlpp::Element::NodeList list = element->get_children();
381         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
382         {
383                 xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
384                 if(!child)
385                         continue;
386                 else
387                 if(child->get_name()=="r")
388                 {
389                         if(child->get_children().empty())
390                         {
391                                 error(element, "Undefined value in <r>");
392                                 return Color();
393                         }
394                         color.set_r(atof(child->get_child_text()->get_content().c_str()));
395                 }
396                 else
397                 if(child->get_name()=="g")
398                 {
399                         if(child->get_children().empty())
400                         {
401                                 error(element, "Undefined value in <g>");
402                                 return Color();
403                         }
404                         color.set_g(atof(child->get_child_text()->get_content().c_str()));
405                 }
406                 else
407                 if(child->get_name()=="b")
408                 {
409                         if(child->get_children().empty())
410                         {
411                                 error(element, "Undefined value in <b>");
412                                 return Color();
413                         }
414                         color.set_b(atof(child->get_child_text()->get_content().c_str()));
415                 }
416                 else
417                 if(child->get_name()=="a")
418                 {
419                         if(child->get_children().empty())
420                         {
421                                 error(element, "Undefined value in <a>");
422                                 return Color();
423                         }
424                         color.set_a(atof(child->get_child_text()->get_content().c_str()));
425                 }
426                 else
427                         error_unexpected_element(child,child->get_name());
428         }
429
430         return color;
431 }
432
433 synfig::String
434 CanvasParser::parse_string(xmlpp::Element *element)
435 {
436         assert(element->get_name()=="string");
437
438         if(element->get_children().empty())
439         {
440                 warning(element, "Undefined value in <string>");
441                 return synfig::String();
442         }
443
444         if(element->get_child_text()->get_content().empty())
445         {
446                 warning(element, "Content element of <string> appears to be empty");
447                 return synfig::String();
448         }
449
450         return element->get_child_text()->get_content();
451 }
452
453 bool
454 CanvasParser::parse_bool(xmlpp::Element *element)
455 {
456         assert(element->get_name()=="bool");
457
458         if(!element->get_children().empty())
459                 warning(element, strprintf(_("<%s> should not contain anything"),"bool"));
460
461         if(!element->get_attribute("value"))
462         {
463                 error(element,strprintf(_("<%s> is missing \"value\" attribute"),"bool"));
464                 return false;
465         }
466
467         string val=element->get_attribute("value")->get_value();
468
469         if(val=="true" || val=="1")
470                 return true;
471         if(val=="false" || val=="0")
472                 return false;
473
474         error(element,strprintf(_("Bad value \"%s\" in <%s>"),val.c_str(),"bool"));
475
476         return false;
477 }
478
479 Gradient
480 CanvasParser::parse_gradient(xmlpp::Element *node)
481 {
482         assert(node->get_name()=="gradient");
483         Gradient ret;
484
485         xmlpp::Element::NodeList list = node->get_children();
486         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
487         {
488                 xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
489                 if(!child)
490                         continue;
491                 else
492                 {
493                         Gradient::CPoint cpoint;
494                         cpoint.color=parse_color(child);
495
496                         if(!child->get_attribute("pos"))
497                         {
498                                 error(child,strprintf(_("<%s> is missing \"pos\" attribute"),"gradient"));
499                                 return Gradient();
500                         }
501
502                         cpoint.pos=atof(child->get_attribute("pos")->get_value().c_str());
503
504                         ret.push_back(cpoint);
505                 }
506         }
507         ret.sort();
508         return ret;
509 }
510
511 ValueBase
512 CanvasParser::parse_list(xmlpp::Element *element,Canvas::Handle canvas)
513 {
514         vector<ValueBase> value_list;
515
516         xmlpp::Element::NodeList list = element->get_children();
517         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
518         {
519                 xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
520                 if(!child)
521                         continue;
522                 else
523                 {
524                         value_list.push_back(parse_value(child,canvas));
525                         if(!value_list.back().is_valid())
526                         {
527                                 value_list.pop_back();
528                                 error(child,"Bad ValueBase");
529                                 continue;
530                         }
531                 }
532         }
533         return value_list;
534 }
535
536 Segment
537 CanvasParser::parse_segment(xmlpp::Element *element)
538 {
539         assert(element->get_name()=="segment");
540
541         if(element->get_children().empty())
542         {
543                 error(element, "Undefined value in <segment>");
544                 return Segment();
545         }
546
547         Segment seg;
548
549         xmlpp::Element::NodeList list = element->get_children();
550         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
551         {
552                 xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
553                 if(!child)
554                         continue;
555                 else
556                 if(child->get_name()=="p1")
557                 {
558                         xmlpp::Element::NodeList list = child->get_children();
559                         xmlpp::Element::NodeList::iterator iter;
560
561                         // Search for the first non-text XML element
562                         for(iter = list.begin(); iter != list.end(); ++iter)
563                                 if(dynamic_cast<xmlpp::Element*>(*iter)) break;
564
565                         if(iter==list.end())
566                         {
567                                 error(element, "Undefined value in <p1>");
568                                 continue;
569                         }
570
571                         if((*iter)->get_name()!="vector")
572                         {
573                                 error_unexpected_element((*iter),(*iter)->get_name(),"vector");
574                                 continue;
575                         }
576
577                         seg.p1=parse_vector(dynamic_cast<xmlpp::Element*>(*iter));
578                 }
579                 else
580                 if(child->get_name()=="t1")
581                 {
582                         xmlpp::Element::NodeList list = child->get_children();
583                         xmlpp::Element::NodeList::iterator iter;
584
585                         // Search for the first non-text XML element
586                         for(iter = list.begin(); iter != list.end(); ++iter)
587                                 if(dynamic_cast<xmlpp::Element*>(*iter)) break;
588
589                         if(iter==list.end())
590                         {
591                                 error(element, "Undefined value in <t1>");
592                                 continue;
593                         }
594
595                         if((*iter)->get_name()!="vector")
596                         {
597                                 error_unexpected_element((*iter),(*iter)->get_name(),"vector");
598                                 continue;
599                         }
600
601                         seg.t1=parse_vector(dynamic_cast<xmlpp::Element*>(*iter));
602                 }
603                 else
604                 if(child->get_name()=="p2")
605                 {
606                         xmlpp::Element::NodeList list = child->get_children();
607                         xmlpp::Element::NodeList::iterator iter;
608
609                         // Search for the first non-text XML element
610                         for(iter = list.begin(); iter != list.end(); ++iter)
611                                 if(dynamic_cast<xmlpp::Element*>(*iter)) break;
612
613                         if(iter==list.end())
614                         {
615                                 error(element, "Undefined value in <p2>");
616                                 continue;
617                         }
618
619                         if((*iter)->get_name()!="vector")
620                         {
621                                 error_unexpected_element((*iter),(*iter)->get_name(),"vector");
622                                 continue;
623                         }
624
625                         seg.p2=parse_vector(dynamic_cast<xmlpp::Element*>(*iter));
626                 }
627                 else
628                 if(child->get_name()=="t2")
629                 {
630                         xmlpp::Element::NodeList list = child->get_children();
631                         xmlpp::Element::NodeList::iterator iter;
632
633                         // Search for the first non-text XML element
634                         for(iter = list.begin(); iter != list.end(); ++iter)
635                                 if(dynamic_cast<xmlpp::Element*>(*iter)) break;
636
637                         if(iter==list.end())
638                         {
639                                 error(element, "Undefined value in <t2>");
640                                 continue;
641                         }
642
643                         if((*iter)->get_name()!="vector")
644                         {
645                                 error_unexpected_element((*iter),(*iter)->get_name(),"vector");
646                                 continue;
647                         }
648
649                         seg.t2=parse_vector(dynamic_cast<xmlpp::Element*>(*iter));
650                 }
651                 else
652                         error_unexpected_element(child,child->get_name());
653         }
654         return seg;
655 }
656
657 BLinePoint
658 CanvasParser::parse_bline_point(xmlpp::Element *element)
659 {
660         assert(element->get_name()=="bline_point");
661         if(element->get_children().empty())
662         {
663                 error(element, "Undefined value in <bline_point>");
664                 return BLinePoint();
665         }
666
667         BLinePoint ret;
668         ret.set_split_tangent_flag(false);
669
670         xmlpp::Element::NodeList list = element->get_children();
671         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
672         {
673                 xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
674                 if(!child)
675                         continue;
676                 else
677                 // Vertex
678                 if(child->get_name()[0]=='v' || child->get_name()=="p1")
679                 {
680                         xmlpp::Element::NodeList list = child->get_children();
681                         xmlpp::Element::NodeList::iterator iter;
682
683                         // Search for the first non-text XML element
684                         for(iter = list.begin(); iter != list.end(); ++iter)
685                                 if(dynamic_cast<xmlpp::Element*>(*iter)) break;
686
687                         if(iter==list.end())
688                         {
689                                 error(element, "Undefined value in <vertex>");
690                                 continue;
691                         }
692
693                         if((*iter)->get_name()!="vector")
694                         {
695                                 error_unexpected_element((*iter),(*iter)->get_name(),"vector");
696                                 continue;
697                         }
698
699                         ret.set_vertex(parse_vector(dynamic_cast<xmlpp::Element*>(*iter)));
700                 }
701                 else
702                 // Tangent 1
703                 if(child->get_name()=="t1" || child->get_name()=="tangent")
704                 {
705                         xmlpp::Element::NodeList list = child->get_children();
706                         xmlpp::Element::NodeList::iterator iter;
707
708                         // Search for the first non-text XML element
709                         for(iter = list.begin(); iter != list.end(); ++iter)
710                                 if(dynamic_cast<xmlpp::Element*>(*iter)) break;
711
712                         if(iter==list.end())
713                         {
714                                 error(element, "Undefined value in <t1>");
715                                 continue;
716                         }
717
718                         if((*iter)->get_name()!="vector")
719                         {
720                                 error_unexpected_element((*iter),(*iter)->get_name(),"vector");
721                                 continue;
722                         }
723
724                         ret.set_tangent1(parse_vector(dynamic_cast<xmlpp::Element*>(*iter)));
725                 }
726                 else
727                 // Tangent 2
728                 if(child->get_name()=="t2")
729                 {
730                         xmlpp::Element::NodeList list = child->get_children();
731                         xmlpp::Element::NodeList::iterator iter;
732
733                         // Search for the first non-text XML element
734                         for(iter = list.begin(); iter != list.end(); ++iter)
735                                 if(dynamic_cast<xmlpp::Element*>(*iter)) break;
736
737                         if(iter==list.end())
738                         {
739                                 error(element, "Undefined value in <t2>");
740                                 continue;
741                         }
742
743                         if((*iter)->get_name()!="vector")
744                         {
745                                 error_unexpected_element((*iter),(*iter)->get_name(),"vector");
746                                 continue;
747                         }
748
749                         ret.set_tangent2(parse_vector(dynamic_cast<xmlpp::Element*>(*iter)));
750                         ret.set_split_tangent_flag(true);
751                 }
752                 else
753                 // width
754                 if(child->get_name()=="width")
755                 {
756                         xmlpp::Element::NodeList list = child->get_children();
757                         xmlpp::Element::NodeList::iterator iter;
758
759                         // Search for the first non-text XML element
760                         for(iter = list.begin(); iter != list.end(); ++iter)
761                                 if(dynamic_cast<xmlpp::Element*>(*iter)) break;
762
763                         if(iter==list.end())
764                         {
765                                 error(element, "Undefined value in <width>");
766                                 continue;
767                         }
768
769                         if((*iter)->get_name()!="real")
770                         {
771                                 error_unexpected_element((*iter),(*iter)->get_name(),"real");
772                                 continue;
773                         }
774
775                         ret.set_width(parse_real(dynamic_cast<xmlpp::Element*>(*iter)));
776                 }
777                 else
778                 // origin
779                 if(child->get_name()=="origin")
780                 {
781                         xmlpp::Element::NodeList list = child->get_children();
782                         xmlpp::Element::NodeList::iterator iter;
783
784                         // Search for the first non-text XML element
785                         for(iter = list.begin(); iter != list.end(); ++iter)
786                                 if(dynamic_cast<xmlpp::Element*>(*iter)) break;
787
788                         if(iter==list.end())
789                         {
790                                 error(element, "Undefined value in <origin>");
791                                 continue;
792                         }
793
794                         if((*iter)->get_name()!="real")
795                         {
796                                 error_unexpected_element((*iter),(*iter)->get_name(),"real");
797                                 continue;
798                         }
799
800                         ret.set_origin(parse_real(dynamic_cast<xmlpp::Element*>(*iter)));
801                 }
802                 else
803                         error_unexpected_element(child,child->get_name());
804         }
805         return ret;
806 }
807
808 Angle
809 CanvasParser::parse_angle(xmlpp::Element *element)
810 {
811         assert(element->get_name()=="angle");
812
813         if(!element->get_children().empty())
814                 warning(element, strprintf(_("<%s> should not contain anything"),"angle"));
815
816         if(!element->get_attribute("value"))
817         {
818                 error(element,strprintf(_("<%s> is missing \"value\" attribute"),"angle"));
819                 return Angle();
820         }
821
822         string val=element->get_attribute("value")->get_value();
823
824         return Angle::deg(atof(val.c_str()));
825 }
826
827 bool
828 CanvasParser::parse_static(xmlpp::Element *element)
829 {
830         if(!element->get_attribute("static"))
831                 return false;
832
833         string val=element->get_attribute("static")->get_value();
834
835         if(val=="true" || val=="1")
836                 return true;
837         if(val=="false" || val=="0")
838                 return false;
839
840         error(element,strprintf(_("Bad value \"%s\" in <%s>"),val.c_str(),"bool"));
841
842         return false;
843 }
844
845
846 ValueBase
847 CanvasParser::parse_value(xmlpp::Element *element,Canvas::Handle canvas)
848 {
849         if(element->get_name()=="real")
850         {
851                 ValueBase ret;
852                 ret.set(parse_real(element));
853                 ret.set_static(parse_static(element));
854                 return ret;
855         }
856         else
857         if(element->get_name()=="time")
858                 return parse_time(element,canvas);
859         else
860         if(element->get_name()=="integer")
861                 return parse_integer(element);
862         else
863         if(element->get_name()=="string")
864                 return parse_string(element);
865         else
866         if(element->get_name()=="vector")
867                 return parse_vector(element);
868         else
869         if(element->get_name()=="color")
870                 return parse_color(element);
871         else
872         if(element->get_name()=="segment")
873                 return parse_segment(element);
874         else
875         if(element->get_name()=="list")
876                 return parse_list(element,canvas);
877         else
878         if(element->get_name()=="gradient")
879                 return parse_gradient(element);
880         else
881         if(element->get_name()=="bool")
882                 return parse_bool(element);
883         else
884         //if(element->get_name()=="canvas")
885         //      return parse_canvas(element,canvas,true);       // inline canvas
886         //else
887         if(element->get_name()=="angle" || element->get_name()=="degrees" || element->get_name()=="radians" || element->get_name()=="rotations")
888                 return parse_angle(element);
889         else
890         if(element->get_name()=="bline_point")
891                 return parse_bline_point(element);
892         else
893         if(element->get_name()=="canvas")
894         {
895                 ValueBase ret;
896                 ret.set(parse_canvas(element,canvas,true));
897                 ret.set_static(parse_static(element));
898                 return ret;
899         }
900         else
901         {
902                 error_unexpected_element(element,element->get_name());
903         }
904
905         return ValueBase();
906 }
907
908
909
910
911 ValueNode_Animated::Handle
912 CanvasParser::parse_animated(xmlpp::Element *element,Canvas::Handle canvas)
913 {
914         assert(element->get_name()=="hermite" || element->get_name()=="animated");
915
916         if(!element->get_attribute("type"))
917         {
918                 error(element,"Missing attribute \"type\" in <animated>");
919                 return ValueNode_Animated::Handle();
920         }
921
922         ValueBase::Type type=ValueBase::ident_type(element->get_attribute("type")->get_value());
923
924         if(!type)
925         {
926                 error(element,"Bad type in <animated>");
927                 return ValueNode_Animated::Handle();
928         }
929
930         ValueNode_Animated::Handle value_node=ValueNode_Animated::create(type);
931
932         if(!value_node)
933         {
934                 error(element,strprintf(_("Unable to create <animated> with type \"%s\""),ValueBase::type_local_name(type).c_str()));
935                 return ValueNode_Animated::Handle();
936         }
937
938         value_node->set_root_canvas(canvas->get_root());
939
940         xmlpp::Element::NodeList list = element->get_children();
941         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
942         {
943                 xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
944                 if(!child)
945                         continue;
946                 else
947                 if(child->get_name()=="waypoint")
948                 {
949                         if(!child->get_attribute("time"))
950                         {
951                                 error(child,_("<waypoint> is missing attribute \"time\""));
952                                 continue;
953                         }
954
955                         Time time(child->get_attribute("time")->get_value(),canvas->rend_desc().get_frame_rate());
956
957
958                         ValueNode::Handle waypoint_value_node;
959                         xmlpp::Element::NodeList list = child->get_children();
960
961                         if(child->get_attribute("use"))
962                         {
963                                 if(!list.empty())
964                                         warning(child,_("Found \"use\" attribute for <waypoint>, but it wasn't empty. Ignoring contents..."));
965
966                                 // the waypoint might look like this, in which case we won't find "mycanvas" in the list of valuenodes, 'cos it's a canvas
967                                 //
968                                 //      <animated type="canvas">
969                                 //        <waypoint time="0s" use="mycanvas"/>
970                                 //      </animated>
971                                 if (type==ValueBase::TYPE_CANVAS)
972                                 {
973                                         String warnings;
974                                         waypoint_value_node=ValueNode_Const::create(canvas->surefind_canvas(child->get_attribute("use")->get_value(), warnings));
975                                         warnings_text += warnings;
976                                 }
977                                 else
978                                         waypoint_value_node=canvas->surefind_value_node(child->get_attribute("use")->get_value());
979                         }
980                         else
981                         {
982                                 if(child->get_children().empty())
983                                 {
984                                         error(child, strprintf(_("<%s> is missing its data"),"waypoint"));
985                                         continue;
986                                 }
987
988                                 xmlpp::Element::NodeList::iterator iter;
989
990                                 // Search for the first non-text XML element
991                                 for(iter = list.begin(); iter != list.end(); ++iter)
992                                         if(dynamic_cast<xmlpp::Element*>(*iter)) break;
993
994                                 if(iter==list.end())
995                                 {
996                                         error(child, strprintf(_("<%s> is missing its data"),"waypoint"));
997                                         continue;
998                                 }
999
1000                                 waypoint_value_node=parse_value_node(dynamic_cast<xmlpp::Element*>(*iter),canvas);
1001
1002                                 /*
1003                                 ValueBase data=parse_value(dynamic_cast<xmlpp::Element*>(*iter),canvas);
1004
1005                                 if(!data.is_valid())
1006                                 {
1007                                         error(child,_("Bad data for <waypoint>"));
1008                                         continue;
1009                                 }
1010                                 */
1011                                 if(!waypoint_value_node)
1012                                 {
1013                                         error(child,_("Bad data for <waypoint>"));
1014                                         continue;
1015                                 }
1016
1017                                 /*! HACK -- This is a temporary fix to help repair some
1018                                 **      weirdness that is currently going on (10-21-2004).
1019                                 **      This short circuits the linking of waypoints,
1020                                 **      a feature which is so obscure that we can get
1021                                 **      away with something like this pretty easily.
1022                                 */
1023                                 waypoint_value_node=waypoint_value_node->clone();
1024
1025                                 // Warn if there is trash after the param value
1026                                 for(iter++; iter != list.end(); ++iter)
1027                                         if(dynamic_cast<xmlpp::Element*>(*iter))
1028                                                 warning((*iter),strprintf(_("Unexpected element <%s> after <waypoint> data, ignoring..."),(*iter)->get_name().c_str()));
1029                         }
1030
1031
1032                         try {
1033                                 ValueNode_Animated::WaypointList::iterator waypoint=value_node->new_waypoint(time,waypoint_value_node);
1034
1035                         if(child->get_attribute("tension"))
1036                         {
1037                                 synfig::String str(child->get_attribute("tension")->get_value());
1038                                 waypoint->set_tension(atof(str.c_str()));
1039                         }
1040                         if(child->get_attribute("temporal-tension"))
1041                         {
1042                                 synfig::String str(child->get_attribute("temporal-tension")->get_value());
1043                                 waypoint->set_temporal_tension(atof(str.c_str()));
1044                         }
1045                         if(child->get_attribute("continuity"))
1046                         {
1047                                 synfig::String str(child->get_attribute("continuity")->get_value());
1048                                 waypoint->set_continuity(atof(str.c_str()));
1049                         }
1050                         if(child->get_attribute("bias"))
1051                         {
1052                                 synfig::String str(child->get_attribute("bias")->get_value());
1053                                 waypoint->set_bias(atof(str.c_str()));
1054                         }
1055
1056                         if(child->get_attribute("before"))
1057                         {
1058                                 string val=child->get_attribute("before")->get_value();
1059                                 if(val=="halt")
1060                                         waypoint->set_before(INTERPOLATION_HALT);
1061                                 else if(val=="constant")
1062                                         waypoint->set_before(INTERPOLATION_CONSTANT);
1063                                 else if(val=="linear")
1064                                         waypoint->set_before(INTERPOLATION_LINEAR);
1065                                 else if(val=="manual")
1066                                         waypoint->set_before(INTERPOLATION_MANUAL);
1067                                 else if(val=="auto")
1068                                         waypoint->set_before(INTERPOLATION_TCB);
1069                                 else
1070                                         error(child,strprintf(_("\"%s\" not a valid value for attribute \"%s\" in <%s>"),val.c_str(),"before","waypoint"));
1071                         }
1072
1073                         if(child->get_attribute("after"))
1074                         {
1075                                 string val=child->get_attribute("after")->get_value();
1076                                 if(val=="halt")
1077                                         waypoint->set_after(INTERPOLATION_HALT);
1078                                 else if(val=="constant")
1079                                         waypoint->set_after(INTERPOLATION_CONSTANT);
1080                                 else if(val=="linear")
1081                                         waypoint->set_after(INTERPOLATION_LINEAR);
1082                                 else if(val=="manual")
1083                                         waypoint->set_after(INTERPOLATION_MANUAL);
1084                                 else if(val=="auto")
1085                                         waypoint->set_after(INTERPOLATION_TCB);
1086                                 else
1087                                         error(child,strprintf(_("\"%s\" not a valid value for attribute \"%s\" in <%s>"),val.c_str(),"before","waypoint"));
1088                         }
1089                         }
1090                         catch(Exception::BadTime x)
1091                         {
1092                                 warning(child,x.what());
1093                         }
1094                         continue;
1095
1096                 }
1097                 else
1098                         error_unexpected_element(child,child->get_name());
1099         }
1100
1101         // in canvas version 0.1, angles used to wrap, so to get from -179
1102         // degrees to 180 degrees meant a 1 degree change
1103         // in canvas version 0.2 they don't, so that's a 359 degree change
1104
1105         // when loading a version 0.1 canvas, modify constant angle
1106         // waypoints to that they are within 180 degrees of the previous
1107         // waypoint's value
1108         if (type == ValueBase::TYPE_ANGLE)
1109         {
1110                 if (canvas->get_version() == "0.1")
1111                 {
1112                         bool first = true;
1113                         Real angle, prev = 0;
1114                         WaypointList &wl = value_node->waypoint_list();
1115                         for (WaypointList::iterator iter = wl.begin(); iter != wl.end(); iter++)
1116                         {
1117                                 angle = Angle::deg(iter->get_value(iter->get_time()).get(Angle())).get();
1118                                 if (first)
1119                                         first = false;
1120                                 else if (iter->get_value_node()->get_name() == "constant")
1121                                 {
1122                                         if (angle - prev > 180)
1123                                         {
1124                                                 while (angle - prev > 180) angle -= 360;
1125                                                 iter->set_value(Angle::deg(angle));
1126                                         }
1127                                         else if (prev - angle > 180)
1128                                         {
1129                                                 while (prev - angle > 180) angle += 360;
1130                                                 iter->set_value(Angle::deg(angle));
1131                                         }
1132                                 }
1133                                 prev = angle;
1134                         }
1135                 }
1136         }
1137
1138         value_node->changed();
1139         return value_node;
1140 }
1141
1142 etl::handle<LinkableValueNode>
1143 CanvasParser::parse_linkable_value_node(xmlpp::Element *element,Canvas::Handle canvas)
1144 {
1145         // Determine the type
1146         if(!element->get_attribute("type"))
1147         {
1148                 error(element, strprintf(_("Missing attribute \"type\" in <%s>"), element->get_name().c_str()));
1149                 return 0;
1150         }
1151
1152         ValueBase::Type type=ValueBase::ident_type(element->get_attribute("type")->get_value());
1153
1154         if(!type)
1155         {
1156                 error(element, strprintf(_("Bad type in <%s>"), element->get_name().c_str()));
1157                 return 0;
1158         }
1159
1160         handle<LinkableValueNode> value_node=LinkableValueNode::create(element->get_name(),type);
1161         handle<ValueNode> c[value_node->link_count()];
1162
1163         if(!value_node)
1164         {
1165                 error(element, strprintf(_("Error creating ValueNode <%s> with type '%s'.  Refer to '%s'"),
1166                                                                  element->get_name().c_str(),
1167                                                                  ValueBase::type_local_name(type).c_str(),
1168                                                                  VALUENODE_COMPATIBILITY_URL));
1169                 return 0;
1170         }
1171
1172         if(value_node->get_type()!=type)
1173         {
1174                 error(element, strprintf(_("<%s> did not accept type '%s'"),
1175                                                                  element->get_name().c_str(),
1176                                                                  ValueBase::type_local_name(type).c_str()));
1177                 return 0;
1178         }
1179
1180         value_node->set_root_canvas(canvas->get_root());
1181
1182         // handle exported valuenodes
1183         {
1184                 int index;
1185                 String id, name;
1186                 xmlpp::Element::AttributeList attrib_list(element->get_attributes());
1187                 for(xmlpp::Element::AttributeList::iterator iter = attrib_list.begin(); iter != attrib_list.end(); iter++)
1188                 {
1189                         name = (*iter)->get_name();
1190                         id = (*iter)->get_value();
1191
1192                         if (name == "guid" || name == "id" || name == "type")
1193                                 continue;
1194
1195                         try {
1196                                 index = value_node->get_link_index_from_name(name);
1197
1198                                 if(c[index])
1199                                 {
1200                                         error(element,strprintf(_("'%s' was already defined in <%s>"),
1201                                                                                         name.c_str(),
1202                                                                                         element->get_name().c_str()));
1203                                         continue;
1204                                 }
1205
1206                                 c[index] = canvas->surefind_value_node(id);
1207
1208                                 if (!c[index])
1209                                 {
1210                                         error(element, strprintf(_("'%s' attribute in <%s> references unknown ID '%s'"),
1211                                                                                          name.c_str(),
1212                                                                                          element->get_name().c_str(),
1213                                                                                          id.c_str()));
1214                                         continue;
1215                                 }
1216
1217                                 if(!value_node->set_link(index, c[index]))
1218                                 {
1219                                         error(element, strprintf(_("Unable to set link '\"%s\" to ValueNode \"%s\" (link #%d in \"%s\")"),
1220                                                                                          value_node->link_name(index).c_str(),
1221                                                                                          id.c_str(),
1222                                                                                          index,
1223                                                                                          element->get_name().c_str()));
1224                                         continue;
1225                                 }
1226
1227                                 // printf("  <%s> set link %d (%s) using exported value\n", element->get_name().c_str(), index, name.c_str());
1228                         }
1229                         catch (Exception::BadLinkName)
1230                         {
1231                                 warning(element, strprintf("Bad link name '%s'", name.c_str()));
1232                         }
1233                         catch(Exception::IDNotFound)
1234                         {
1235                                 error(element,"Unable to resolve " + id);
1236                         }
1237                         catch(Exception::FileNotFound)
1238                         {
1239                                 error(element,"Unable to open file referenced in " + id);
1240                         }
1241                         catch(...)
1242                         {
1243                                 error(element,strprintf(_("Unknown Exception thrown when referencing ValueNode \"%s\""), id.c_str()));
1244                                 throw;
1245                         }
1246                 }
1247         }
1248
1249         // handle inline valuenodes
1250         {
1251                 int index;
1252                 String child_name;
1253                 xmlpp::Element::NodeList list = element->get_children();
1254                 for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
1255                 {
1256                         xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
1257                         try
1258                         {
1259                                 if(!child)
1260                                         continue;
1261
1262                                 child_name = child->get_name();
1263
1264                                 index = value_node->get_link_index_from_name(child_name);
1265
1266                                 if(c[index])
1267                                 {
1268                                         error(child, strprintf(_("'%s' was already defined in <%s>"),
1269                                                                                    child_name.c_str(),
1270                                                                                    element->get_name().c_str()));
1271                                         break;
1272                                 }
1273
1274                                 xmlpp::Element::NodeList list = child->get_children();
1275                                 xmlpp::Element::NodeList::iterator iter;
1276
1277                                 // Search for the first non-text XML element
1278                                 for(iter = list.begin(); iter != list.end(); ++iter)
1279                                         if(dynamic_cast<xmlpp::Element*>(*iter)) break;
1280
1281                                 if(iter==list.end())
1282                                 {
1283                                         error(child,strprintf(_("element <%s> is missing its contents"),
1284                                                                                   child_name.c_str()));
1285                                         continue;
1286                                 }
1287
1288                                 c[index]=parse_value_node(dynamic_cast<xmlpp::Element*>(*iter),canvas);
1289
1290                                 if(!c[index])
1291                                 {
1292                                         error((*iter),strprintf(_("Parse of '%s' failed"),
1293                                                                                         child_name.c_str()));
1294                                         continue;
1295                                 }
1296
1297                                 if(!value_node->set_link(index,c[index]))
1298                                 {
1299                                         error(child,strprintf(_("Unable to connect value node ('%s' of type '%s') to link %d (%s)"),
1300                                                                                   c[index]->get_name().c_str(),
1301                                                                                   ValueBase::type_local_name(c[index]->get_type()).c_str(),
1302                                                                                   index,
1303                                                                                   value_node->link_name(index).c_str()));
1304                                         continue;
1305                                 }
1306
1307                                 // \todo do a search for more elements and warn if they are found
1308
1309                                 // printf("  <%s> set link %d (%s) using inline value\n", element->get_name().c_str(), index, child_name.c_str());
1310                         }
1311                         catch(Exception::BadLinkName)
1312                         {
1313                                 warning(child, strprintf("Bad link name for <%s>", element->get_name().c_str()));
1314                         }
1315                         catch(...)
1316                         {
1317                                 error(child, strprintf(_("Unknown Exception thrown when working on element \"%s\""),child_name.c_str()));
1318                                 throw;
1319                         }
1320                 }
1321         }
1322
1323         String version(canvas->get_version());
1324         for (int i = 0; i < value_node->link_count(); i++)
1325         {
1326                 if (!c[i])
1327                 {
1328                         // the 'width' parameter of <stripes> wasn't always present in version 0.1 canvases
1329                         if (version == "0.1" && element->get_name() == "stripes" && value_node->link_name(i) == "width")
1330                                 continue;
1331
1332                         // these 3 blinecalctangent parameters didn't appear until canvas version 0.5
1333                         if ((version == "0.1" || version == "0.2" || version == "0.3" || version == "0.4") &&
1334                                 element->get_name() == "blinecalctangent" &&
1335                                 (value_node->link_name(i) == "offset" ||
1336                                  value_node->link_name(i) == "scale" ||
1337                                  value_node->link_name(i) == "fixed_length"))
1338                                 continue;
1339
1340                         // 'scale' was added while canvas version 0.5 was in use
1341                         if ((version == "0.3" || version == "0.4" || version == "0.5") &&
1342                                 element->get_name() == "blinecalcwidth" &&
1343                                 value_node->link_name(i) == "scale")
1344                                 continue;
1345
1346                         // 'loop' was added while canvas version 0.5 was in use, as was the 'gradientcolor' node type
1347                         if (version == "0.5" &&
1348                                 element->get_name() == "gradientcolor" &&
1349                                 value_node->link_name(i) == "loop")
1350                                 continue;
1351
1352                         // 'loop' was added while canvas version 0.6 was in use; the 'random' node was added back when 0.1 was in use
1353                         if ((version == "0.1" || version == "0.2" || version == "0.3" || version == "0.4" || version == "0.5" || version == "0.6") &&
1354                                 element->get_name() == "random" &&
1355                                 value_node->link_name(i) == "loop")
1356                                 continue;
1357
1358                         error(element, strprintf(_("<%s> is missing link %d (%s)"),
1359                                                                          element->get_name().c_str(),
1360                                                                          i,
1361                                                                          value_node->link_name(i).c_str()));
1362                         return 0;
1363                 }
1364         }
1365
1366         // pre 0.4 canvases had *calctangent outputs scaled down by 0.5 for some reason
1367         if (element->get_name() == "blinecalctangent" || element->get_name() == "segcalctangent")
1368         {
1369                 if (version == "0.1" || version == "0.2" || version == "0.3")
1370                 {
1371                         handle<LinkableValueNode> scale_value_node=LinkableValueNode::create("scale",type);
1372                         scale_value_node->set_link("link", value_node);
1373                         scale_value_node->set_link("scalar", ValueNode_Const::create(Real(0.5)));
1374                         value_node = scale_value_node;
1375                 }
1376         }
1377
1378         return value_node;
1379 }
1380
1381 // This will also parse a bline
1382 handle<ValueNode_DynamicList>
1383 CanvasParser::parse_dynamic_list(xmlpp::Element *element,Canvas::Handle canvas)
1384 {
1385         assert(element->get_name()=="dynamic_list" || element->get_name()=="bline");
1386
1387         const float fps(canvas?canvas->rend_desc().get_frame_rate():0);
1388
1389         if(!element->get_attribute("type"))
1390         {
1391                 error(element,"Missing attribute \"type\" in <dynamic_list>");
1392                 return handle<ValueNode_DynamicList>();
1393         }
1394
1395         ValueBase::Type type=ValueBase::ident_type(element->get_attribute("type")->get_value());
1396
1397         if(!type)
1398         {
1399                 error(element,"Bad type in <dynamic_list>");
1400                 return handle<ValueNode_DynamicList>();
1401         }
1402
1403         handle<ValueNode_DynamicList> value_node;
1404         handle<ValueNode_BLine> bline_value_node;
1405
1406         if(element->get_name()=="bline")
1407         {
1408                 value_node=bline_value_node=ValueNode_BLine::create();
1409                 if(element->get_attribute("loop"))
1410                 {
1411                         String loop=element->get_attribute("loop")->get_value();
1412                         if(loop=="true" || loop=="1" || loop=="TRUE" || loop=="True")
1413                                 bline_value_node->set_loop(true);
1414                         else
1415                                 bline_value_node->set_loop(false);
1416                 }
1417
1418         }
1419         else
1420                 value_node=ValueNode_DynamicList::create(type);
1421
1422         if(!value_node)
1423         {
1424                 error(element,strprintf(_("Unable to create <dynamic_list>")));
1425                 return handle<ValueNode_DynamicList>();
1426         }
1427
1428         value_node->set_root_canvas(canvas->get_root());
1429
1430         xmlpp::Element::NodeList list = element->get_children();
1431         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
1432         {
1433                 xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
1434                 if(!child)
1435                         continue;
1436                 else
1437                 if(child->get_name()=="entry")
1438                 {
1439                         ValueNode_DynamicList::ListEntry list_entry;
1440
1441                         // Parse begin/end waypoints
1442                         {
1443                                 typedef synfig::ValueNode_DynamicList::ListEntry::Activepoint Activepoint;
1444                                 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
1445
1446                                 String begin_sequence;
1447                                 String end_sequence;
1448
1449                                 ActivepointList &timing_info(list_entry.timing_info);
1450
1451                                 if(child->get_attribute("begin"))
1452                                         begin_sequence=child->get_attribute("begin")->get_value();
1453
1454                                 if(child->get_attribute("on"))
1455                                         begin_sequence=child->get_attribute("on")->get_value();
1456
1457                                 if(child->get_attribute("end"))
1458                                         end_sequence=child->get_attribute("end")->get_value();
1459
1460                                 if(child->get_attribute("off"))
1461                                         end_sequence=child->get_attribute("off")->get_value();
1462
1463                                 // clear out any auto-start
1464                                 if(!begin_sequence.empty())
1465                                         timing_info.clear();
1466
1467                                 //! \optimize
1468                                 while(!begin_sequence.empty())
1469                                 {
1470                                         String::iterator iter(find(begin_sequence.begin(),begin_sequence.end(),','));
1471                                         String timecode(begin_sequence.begin(), iter);
1472                                         int priority=0;
1473
1474                                         // skip whitespace before checking for a priority
1475                                         while (isspace(timecode[0]))
1476                                                 timecode=timecode.substr(1);
1477
1478                                         // If there is a priority, then grab it and remove
1479                                         // it from the timecode
1480                                         if(timecode[0]=='p')
1481                                         {
1482                                                 //priority=timecode[1]-'0';
1483                                                 //timecode=String(timecode.begin()+3,timecode.end());
1484                                                 int space=timecode.find_first_of(' ');
1485                                                 priority=atoi(String(timecode,1,space-1).c_str());
1486                                                 timecode=String(timecode.begin()+space+1,timecode.end());
1487                                                 //synfig::info("priority: %d      timecode: %s",priority,timecode.c_str());
1488                                         }
1489
1490                                         timing_info.push_back(
1491                                                 Activepoint(
1492                                                         Time(
1493                                                                 timecode,
1494                                                                 fps
1495                                                         ),
1496                                                         true,   // Mark as a "on" activepoint
1497                                                         priority
1498                                                 )
1499                                         );
1500
1501                                         if(iter==begin_sequence.end())
1502                                                 begin_sequence.clear();
1503                                         else
1504                                                 begin_sequence=String(iter+1,begin_sequence.end());
1505                                 }
1506
1507                                 //! \optimize
1508                                 while(!end_sequence.empty())
1509                                 {
1510                                         String::iterator iter(find(end_sequence.begin(),end_sequence.end(),','));
1511                                         String timecode(end_sequence.begin(),   iter);
1512                                         int priority=0;
1513
1514                                         // skip whitespace before checking for a priority
1515                                         while (isspace(timecode[0]))
1516                                                 timecode=timecode.substr(1);
1517
1518                                         // If there is a priority, then grab it and remove
1519                                         // it from the timecode
1520                                         if(timecode[0]=='p')
1521                                         {
1522                                                 //priority=timecode[1]-'0';
1523                                                 //timecode=String(timecode.begin()+3,timecode.end());
1524                                                 int space=timecode.find_first_of(' ');
1525                                                 priority=atoi(String(timecode,1,space-1).c_str());
1526                                                 timecode=String(timecode.begin()+space+1,timecode.end());
1527                                                 //synfig::info("priority: %d      timecode: %s",priority,timecode.c_str());
1528                                         }
1529
1530                                         timing_info.push_back(
1531                                                 Activepoint(
1532                                                         Time(
1533                                                                 timecode,
1534                                                                 fps
1535                                                         ),
1536                                                         false,  // Mark as a "off" activepoint
1537                                                         priority
1538                                                 )
1539                                         );
1540                                         if(iter==end_sequence.end())
1541                                                 end_sequence.clear();
1542                                         else
1543                                                 end_sequence=String(iter+1,end_sequence.end());
1544                                 }
1545
1546                                 timing_info.sort();
1547                         }
1548
1549                         if(child->get_attribute("use"))
1550                         {
1551                                 // \todo does this need to be able to read 'use="canvas"', like waypoints can now?  (see 'surefind_canvas' in this file)
1552                                 string id=child->get_attribute("use")->get_value();
1553                                 try
1554                                 {
1555                                         list_entry.value_node=canvas->surefind_value_node(id);
1556                                 }
1557                                 catch(Exception::IDNotFound)
1558                                 {
1559                                         error(child,"\"use\" attribute in <entry> references unknown ID -- "+id);
1560                                         continue;
1561                                 }
1562                         }
1563                         else
1564                         {
1565                                 xmlpp::Element::NodeList list = child->get_children();
1566                                 xmlpp::Element::NodeList::iterator iter;
1567
1568                                 // Search for the first non-text XML element
1569                                 for(iter = list.begin(); iter != list.end(); ++iter)
1570                                         if(dynamic_cast<xmlpp::Element*>(*iter)) break;
1571
1572                                 if(iter==list.end())
1573                                 {
1574                                         error(child,strprintf(_("<entry> is missing its contents or missing \"use\" element")));
1575                                         continue;
1576                                 }
1577
1578                                 list_entry.value_node=parse_value_node(dynamic_cast<xmlpp::Element*>(*iter),canvas);
1579
1580                                 if(!list_entry.value_node)
1581                                         error((*iter),"Parse of ValueNode failed");
1582
1583                                 // \todo do a search for more elements and warn if they are found
1584
1585                         }
1586
1587                         value_node->add(list_entry);
1588                         value_node->set_link(value_node->link_count()-1,list_entry.value_node);
1589                 }
1590                 else
1591                         error_unexpected_element(child,child->get_name());
1592         }
1593         return value_node;
1594 }
1595
1596 handle<ValueNode>
1597 CanvasParser::parse_value_node(xmlpp::Element *element,Canvas::Handle canvas)
1598 {
1599         handle<ValueNode> value_node;
1600         assert(element);
1601
1602         GUID guid;
1603
1604         if(element->get_attribute("guid"))
1605         {
1606                 guid=GUID(element->get_attribute("guid")->get_value())^canvas->get_root()->get_guid();
1607                 value_node=guid_cast<ValueNode>(guid);
1608                 if(value_node)
1609                         return value_node;
1610         }
1611
1612         // If ValueBase::ident_type() recognizes the name, then we know it's a ValueBase
1613         if(element->get_name()!="canvas" && ValueBase::ident_type(element->get_name()))
1614         {
1615                 ValueBase data=parse_value(element,canvas);
1616
1617                 if(!data.is_valid())
1618                 {
1619                         error(element,strprintf(_("Bad data in <%s>"),element->get_name().c_str()));
1620                         return value_node;
1621                 }
1622
1623                 // We want to convert this ValueBase into a
1624                 // ValueNode_Const. That way, we can treat the
1625                 // ID like any other Datanode. Think of this
1626                 // as a shorthand for creating constant ValueNodes.
1627
1628                 value_node=ValueNode_Const::create(data);
1629         }
1630         else
1631         if(element->get_name()=="hermite" || element->get_name()=="animated")
1632                 value_node=parse_animated(element,canvas);
1633         else
1634         if(element->get_name()=="dynamic_list")
1635                 value_node=parse_dynamic_list(element,canvas);
1636         else
1637         if(element->get_name()=="bline") // This is not a typo. The dynamic list parser will parse a bline.
1638                 value_node=parse_dynamic_list(element,canvas);
1639         else
1640         if(LinkableValueNode::book().count(element->get_name()))
1641         {
1642                 value_node=parse_linkable_value_node(element,canvas);
1643                 if (!value_node) value_node = PlaceholderValueNode::create();
1644         }
1645         else
1646         if(element->get_name()=="canvas")
1647                 value_node=ValueNode_Const::create(parse_canvas(element,canvas,true));
1648         else
1649         {
1650                 error_unexpected_element(element,element->get_name());
1651                 error(element, strprintf(_("Expected a ValueNode.  Refer to '%s'"),
1652                                                                  VALUENODE_COMPATIBILITY_URL));
1653                 value_node=PlaceholderValueNode::create();
1654         }
1655
1656         value_node->set_root_canvas(canvas->get_root());
1657
1658         // If we were successful, and our element has
1659         // an ID attribute, go ahead and add it to the
1660         // value_node list
1661         if(value_node && element->get_attribute("id"))
1662         {
1663                 string id=element->get_attribute("id")->get_value();
1664
1665                 //value_node->set_id(id);
1666
1667                 // If there is already a value_node in the list
1668                 // with the same ID, then that is an error
1669                 try { canvas->add_value_node(value_node,id); }
1670                 catch(Exception::BadLinkName)
1671                 {
1672                         warning(element,strprintf(_("Bad ID \"%s\""),id.c_str()));
1673                         return value_node;
1674                 }
1675                 catch(Exception::IDAlreadyExists)
1676                 {
1677                         error(element,strprintf(_("Duplicate ID \"%s\""),id.c_str()));
1678                         return value_node;
1679                 }
1680                 catch(...)
1681                 {
1682                         error(element,strprintf(_("Unknown Exception thrown when adding ValueNode \"%s\""),id.c_str()));
1683                         throw;
1684                 }
1685         }
1686         value_node->set_guid(guid);
1687         return value_node;
1688 }
1689
1690 void
1691 CanvasParser::parse_canvas_defs(xmlpp::Element *element,Canvas::Handle canvas)
1692 {
1693         assert(element->get_name()=="defs");
1694         xmlpp::Element::NodeList list = element->get_children();
1695         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
1696         {
1697                 xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
1698                 if(!child)
1699                         continue;
1700                 else
1701                 if(child->get_name()=="canvas")
1702                         parse_canvas(child, canvas);
1703                 else
1704                         parse_value_node(child,canvas);
1705         }
1706 }
1707
1708 Layer::Handle
1709 CanvasParser::parse_layer(xmlpp::Element *element,Canvas::Handle canvas)
1710 {
1711
1712         assert(element->get_name()=="layer");
1713         Layer::Handle layer;
1714
1715         if(!element->get_attribute("type"))
1716         {
1717                 error(element,_("Missing \"type\" attribute to \"layer\" element"));
1718                 return Layer::Handle();
1719         }
1720
1721         layer=Layer::create(element->get_attribute("type")->get_value());
1722         layer->set_canvas(canvas);
1723
1724         if(element->get_attribute("group"))
1725         {
1726                 layer->add_to_group(
1727                         element->get_attribute("group")->get_value()
1728                 );
1729         }
1730
1731         // Handle the version attribute
1732         if(element->get_attribute("version"))
1733         {
1734                 String version(element->get_attribute("version")->get_value());
1735                 if(version>layer->get_version())
1736                         warning(element,_("Installed layer version is smaller than layer version in file"));
1737                 if(version!=layer->get_version())
1738                         layer->set_version(version);
1739         }
1740
1741         // Handle the description
1742         if(element->get_attribute("desc"))
1743                 layer->set_description(element->get_attribute("desc")->get_value());
1744
1745         if(element->get_attribute("active"))
1746                 layer->set_active(element->get_attribute("active")->get_value()=="false"?false:true);
1747
1748         xmlpp::Element::NodeList list = element->get_children();
1749         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
1750         {
1751                 xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
1752                 if(!child)
1753                         continue;
1754                 else
1755                 if(child->get_name()=="name")
1756                         warning(child,_("<name> entry for <layer> is not yet supported. Ignoring..."));
1757                 else
1758                 if(child->get_name()=="desc")
1759                         warning(child,_("<desc> entry for <layer> is not yet supported. Ignoring..."));
1760                 else
1761                 if(child->get_name()=="param")
1762                 {
1763                         xmlpp::Element::NodeList list = child->get_children();
1764
1765                         if(!child->get_attribute("name"))
1766                         {
1767                                 error(child,_("Missing \"name\" attribute for <param>."));
1768                                 continue;
1769                         }
1770
1771                         String param_name=child->get_attribute("name")->get_value();
1772
1773                         // SVN r2013 and r2014 renamed all 'pos' and 'offset' parameters to 'origin'
1774                         // 'pos' and 'offset' will appear in old .sif files; handle them correctly
1775                         if (param_name == "pos" || param_name == "offset")
1776                                 param_name = "origin";
1777
1778                         if(child->get_attribute("use"))
1779                         {
1780                                 // If the "use" attribute is used, then the
1781                                 // element should be empty. Warn the user if
1782                                 // we find otherwise.
1783                                 if(!list.empty())
1784                                         warning(child,_("Found \"use\" attribute for <param>, but it wasn't empty. Ignoring contents..."));
1785
1786                                 String str=     child->get_attribute("use")->get_value();
1787
1788                                 if (str.empty())
1789                                         error(child,_("Empty use=\"\" value in <param>"));
1790                                 else if(layer->get_param(param_name).get_type()==ValueBase::TYPE_CANVAS)
1791                                 {
1792                                         String warnings;
1793                                         Canvas::Handle c(canvas->surefind_canvas(str, warnings));
1794                                         warnings_text += warnings;
1795                                         if(!c) error((*iter),strprintf(_("Failed to load subcanvas '%s'"), str.c_str()));
1796                                         if(!layer->set_param(param_name,c))
1797                                                 error((*iter),_("Layer rejected canvas link"));
1798                                         //Parse the static option and sets it to the canvas ValueBase
1799                                         layer->set_param_static(param_name, parse_static(child));
1800                                 }
1801                                 else
1802                                 try
1803                                 {
1804                                         handle<ValueNode> value_node=canvas->surefind_value_node(str);
1805
1806                                         // Assign the value_node to the dynamic parameter list
1807                                         if (param_name == "segment_list" && (layer->get_name() == "region" || layer->get_name() == "outline"))
1808                                         {
1809                                                 synfig::warning("%s: Updated valuenode connection to use the \"bline\" parameter instead of \"segment_list\".",
1810                                                                                 layer->get_name().c_str());
1811                                                 param_name = "bline";
1812                                         }
1813                                         layer->connect_dynamic_param(param_name,value_node);
1814                         }
1815                                 catch(Exception::IDNotFound)
1816                                 {
1817                                         error(child,strprintf(_("Unknown ID (%s) referenced in <param>"),str.c_str()));
1818                                 }
1819
1820                                 continue;
1821                         }
1822
1823                         xmlpp::Element::NodeList::iterator iter;
1824
1825                         // Search for the first non-text XML element
1826                         for(iter = list.begin(); iter != list.end(); ++iter)
1827                                 if(dynamic_cast<xmlpp::Element*>(*iter))
1828                                         break;
1829                                 //if(!(!dynamic_cast<xmlpp::Element*>(*iter) && (*iter)->get_name()=="text"||(*iter)->get_name()=="comment"   )) break;
1830
1831                         if(iter==list.end())
1832                         {
1833                                 error(child,_("<param> is either missing its contents, or missing a \"use\" attribute."));
1834                                 continue;
1835                         }
1836
1837                         // If we recognize the element name as a
1838                         // ValueBase, then treat is at one
1839                         if(/*(*iter)->get_name()!="canvas" && */ValueBase::ident_type((*iter)->get_name()) && !dynamic_cast<xmlpp::Element*>(*iter)->get_attribute("guid"))
1840                         {
1841                                 ValueBase data=parse_value(dynamic_cast<xmlpp::Element*>(*iter),canvas);
1842
1843                                 if(!data.is_valid())
1844                                 {
1845                                         error((*iter),_("Bad data for <param>"));
1846                                         continue;
1847                                 }
1848
1849                                 // Set the layer's parameter, and make sure that
1850                                 // the layer liked it
1851                                 if(!layer->set_param(param_name,data))
1852                                 {
1853                                         warning((*iter),strprintf(_("Layer '%s' rejected value for parameter '%s'"),
1854                                                                                           element->get_attribute("type")->get_value().c_str(),
1855                                                                                           param_name.c_str()));
1856                                         continue;
1857                                 }
1858                         }
1859                         else    // ... otherwise, we assume that it is a ValueNode
1860                         {
1861                                 handle<ValueNode> value_node=parse_value_node(dynamic_cast<xmlpp::Element*>(*iter),canvas);
1862
1863                                 if(!value_node)
1864                                 {
1865                                         error((*iter),_("Bad data for <param>"));
1866                                         continue;
1867                                 }
1868
1869                                 // Assign the value_node to the dynamic parameter list
1870                                 layer->connect_dynamic_param(param_name,value_node);
1871                         }
1872
1873                         // Warn if there is trash after the param value
1874                         for(iter++; iter != list.end(); ++iter)
1875                                 if(dynamic_cast<xmlpp::Element*>(*iter))
1876                                         warning((*iter),strprintf(_("Unexpected element <%s> after <param> data, ignoring..."),(*iter)->get_name().c_str()));
1877                         continue;
1878                 }
1879                 else
1880                         error_unexpected_element(child,child->get_name());
1881         }
1882
1883         layer->reset_version();
1884         return layer;
1885 }
1886
1887 Canvas::Handle
1888 CanvasParser::parse_canvas(xmlpp::Element *element,Canvas::Handle parent,bool inline_, String filename)
1889 {
1890
1891         if(element->get_name()!="canvas")
1892         {
1893                 error_unexpected_element(element,element->get_name(),"canvas");
1894                 return Canvas::Handle();
1895         }
1896         Canvas::Handle canvas;
1897
1898
1899
1900         if(parent && (element->get_attribute("id") || inline_))
1901         {
1902                 if(inline_)
1903                 {
1904                         canvas=Canvas::create_inline(parent);
1905                 }
1906                 else
1907                 {
1908                         try
1909                         {
1910                                 String warnings;
1911                                 canvas=parent->find_canvas(element->get_attribute("id")->get_value(), warnings);
1912                                 warnings_text += warnings;
1913                         }
1914                         catch(...)
1915                         {
1916                                 canvas=parent->new_child_canvas(element->get_attribute("id")->get_value());
1917                         }
1918                 }
1919                 canvas->rend_desc().clear_flags();
1920         }
1921         else
1922         {
1923                 canvas=Canvas::create();
1924                 if(filename=="/dev/stdin")
1925                         canvas->set_file_name("./stdin.sif");
1926                 else
1927                         canvas->set_file_name(filename);
1928                 canvas->rend_desc().clear_flags();
1929         }
1930
1931         if(element->get_attribute("guid"))
1932         {
1933                 GUID guid(element->get_attribute("guid")->get_value());
1934                 if(guid_cast<Canvas>(guid))
1935                         return guid_cast<Canvas>(guid);
1936                 else
1937                         canvas->set_guid(guid);
1938         }
1939
1940         if(element->get_attribute("version"))
1941                 canvas->set_version(element->get_attribute("version")->get_value());
1942         else if(parent)
1943                 canvas->set_version(parent->get_version());
1944
1945         if(element->get_attribute("width"))
1946         {
1947                 int width = atoi(element->get_attribute("width")->get_value().c_str());
1948                 if (width < 1)
1949                         fatal_error(element, _("Canvas with width or height less than one is not allowed"));
1950                 canvas->rend_desc().set_w(width);
1951         }
1952
1953         if(element->get_attribute("height"))
1954         {
1955                 int height = atoi(element->get_attribute("height")->get_value().c_str());
1956                 if (height < 1)
1957                         fatal_error(element, _("Canvas with width or height less than one is not allowed"));
1958                 canvas->rend_desc().set_h(height);
1959         }
1960
1961         if(element->get_attribute("xres"))
1962                 canvas->rend_desc().set_x_res(atof(element->get_attribute("xres")->get_value().c_str()));
1963
1964         if(element->get_attribute("yres"))
1965                 canvas->rend_desc().set_y_res(atof(element->get_attribute("yres")->get_value().c_str()));
1966
1967
1968         if(element->get_attribute("fps"))
1969                 canvas->rend_desc().set_frame_rate(atof(element->get_attribute("fps")->get_value().c_str()));
1970
1971         if(element->get_attribute("start-time"))
1972                 canvas->rend_desc().set_time_start(Time(element->get_attribute("start-time")->get_value(),canvas->rend_desc().get_frame_rate()));
1973
1974         if(element->get_attribute("begin-time"))
1975                 canvas->rend_desc().set_time_start(Time(element->get_attribute("begin-time")->get_value(),canvas->rend_desc().get_frame_rate()));
1976
1977         if(element->get_attribute("end-time"))
1978                 canvas->rend_desc().set_time_end(Time(element->get_attribute("end-time")->get_value(),canvas->rend_desc().get_frame_rate()));
1979
1980         if(element->get_attribute("antialias"))
1981                 canvas->rend_desc().set_antialias(atoi(element->get_attribute("antialias")->get_value().c_str()));
1982
1983         if(element->get_attribute("view-box"))
1984         {
1985                 string values=element->get_attribute("view-box")->get_value();
1986                 Vector
1987                         tl,
1988                         br;
1989                 tl[0]=atof(string(values.data(),values.find(' ')).c_str());
1990                 values=string(values.begin()+values.find(' ')+1,values.end());
1991                 tl[1]=atof(string(values.data(),values.find(' ')).c_str());
1992                 values=string(values.begin()+values.find(' ')+1,values.end());
1993                 br[0]=atof(string(values.data(),values.find(' ')).c_str());
1994                 values=string(values.begin()+values.find(' ')+1,values.end());
1995                 br[1]=atof(values.c_str());
1996
1997                 canvas->rend_desc().set_tl(tl);
1998                 canvas->rend_desc().set_br(br);
1999         }
2000
2001         if(element->get_attribute("bgcolor"))
2002         {
2003                 string values=element->get_attribute("bgcolor")->get_value();
2004                 Color bg;
2005
2006                 bg.set_r(atof(string(values.data(),values.find(' ')).c_str()));
2007                 values=string(values.begin()+values.find(' ')+1,values.end());
2008
2009                 bg.set_g(atof(string(values.data(),values.find(' ')).c_str()));
2010                 values=string(values.begin()+values.find(' ')+1,values.end());
2011
2012                 bg.set_b(atof(string(values.data(),values.find(' ')).c_str()));
2013                 values=string(values.begin()+values.find(' ')+1,values.end());
2014
2015                 bg.set_a(atof(values.c_str()));
2016
2017                 canvas->rend_desc().set_bg_color(bg);
2018         }
2019
2020         if(element->get_attribute("focus"))
2021         {
2022                 string values=element->get_attribute("focus")->get_value();
2023                 Vector focus;
2024
2025                 focus[0]=atof(string(values.data(),values.find(' ')).c_str());
2026                 values=string(values.begin()+values.find(' ')+1,values.end());
2027                 focus[1]=atof(values.c_str());
2028
2029                 canvas->rend_desc().set_focus(focus);
2030         }
2031
2032         canvas->rend_desc().set_flags(RendDesc::PX_ASPECT|RendDesc::IM_SPAN);
2033
2034         xmlpp::Element::NodeList list = element->get_children();
2035         for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
2036         {
2037                 xmlpp::Element *child(dynamic_cast<xmlpp::Element*>(*iter));
2038                 if(child)
2039                 {
2040                         if(child->get_name()=="defs")
2041                         {
2042                                 if(canvas->is_inline())
2043                                         error(child,_("Inline canvas cannot have a <defs> section"));
2044                                 parse_canvas_defs(child, canvas);
2045                         }
2046                         else
2047                         if(child->get_name()=="keyframe")
2048                         {
2049                                 if(canvas->is_inline())
2050                                 {
2051                                         warning(child,_("Inline canvas cannot have keyframes"));
2052                                         continue;
2053                                 }
2054
2055                                 canvas->keyframe_list().add(parse_keyframe(child,canvas));
2056                                 canvas->keyframe_list().sync();
2057                         }
2058                         else
2059                         if(child->get_name()=="meta")
2060                         {
2061                                 if(canvas->is_inline())
2062                                 {
2063                                         warning(child,_("Inline canvases cannot have metadata"));
2064                                         continue;
2065                                 }
2066
2067                                 String name,content;
2068
2069                                 if(!child->get_attribute("name"))
2070                                 {
2071                                         warning(child,_("<meta> must have a name"));
2072                                         continue;
2073                                 }
2074
2075                                 if(!child->get_attribute("content"))
2076                                 {
2077                                         warning(child,_("<meta> must have content"));
2078                                         continue;
2079                                 }
2080
2081                                 canvas->set_meta_data(child->get_attribute("name")->get_value(),child->get_attribute("content")->get_value());
2082                         }
2083                         else if(child->get_name()=="name")
2084                         {
2085                                 xmlpp::Element::NodeList list = child->get_children();
2086
2087                                 // If we don't have any name, warn
2088                                 if(list.empty())
2089                                         warning(child,_("blank \"name\" entity"));
2090
2091                                 string tmp;
2092                                 for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
2093                                         if(dynamic_cast<xmlpp::TextNode*>(*iter))tmp+=dynamic_cast<xmlpp::TextNode*>(*iter)->get_content();
2094                                 canvas->set_name(tmp);
2095                         }
2096                         else
2097                         if(child->get_name()=="desc")
2098                         {
2099
2100                                 xmlpp::Element::NodeList list = child->get_children();
2101
2102                                 // If we don't have any description, warn
2103                                 if(list.empty())
2104                                         warning(child,_("blank \"desc\" entity"));
2105
2106                                 string tmp;
2107                                 for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
2108                                         if(dynamic_cast<xmlpp::TextNode*>(*iter))tmp+=dynamic_cast<xmlpp::TextNode*>(*iter)->get_content();
2109                                 canvas->set_description(tmp);
2110                         }
2111                         else
2112                         if(child->get_name()=="author")
2113                         {
2114
2115                                 xmlpp::Element::NodeList list = child->get_children();
2116
2117                                 // If we don't have any description, warn
2118                                 if(list.empty())
2119                                         warning(child,_("blank \"author\" entity"));
2120
2121                                 string tmp;
2122                                 for(xmlpp::Element::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
2123                                         if(dynamic_cast<xmlpp::TextNode*>(*iter))tmp+=dynamic_cast<xmlpp::TextNode*>(*iter)->get_content();
2124                                 canvas->set_author(tmp);
2125                         }
2126                         else
2127                         if(child->get_name()=="layer")
2128                         {
2129                                 //if(canvas->is_inline())
2130                                 //      canvas->push_front(parse_layer(child,canvas->parent()));
2131                                 //else
2132                                         canvas->push_front(parse_layer(child,canvas));
2133                         }
2134                         else
2135                                 error_unexpected_element(child,child->get_name());
2136                 }
2137 //              else
2138 //              if((child->get_name()=="text"||child->get_name()=="comment") && child->has_child_text())
2139 //                      continue;
2140         }
2141
2142         if(canvas->value_node_list().placeholder_count())
2143         {
2144                 String nodes;
2145                 for (ValueNodeList::const_iterator iter = canvas->value_node_list().begin(); iter != canvas->value_node_list().end(); iter++)
2146                         if(PlaceholderValueNode::Handle::cast_dynamic(*iter))
2147                         {
2148                                 if (nodes != "") nodes += ", ";
2149                                 nodes += "'" + (*iter)->get_id() + "'";
2150                         }
2151                 error(element,strprintf(_("Canvas '%s' has undefined %s: %s"),
2152                                                                 canvas->get_id().c_str(),
2153                                                                 canvas->value_node_list().placeholder_count() == 1 ? _("ValueNode") : _("ValueNodes"),
2154                                                                 nodes.c_str()));
2155         }
2156
2157         canvas->set_version(CURRENT_CANVAS_VERSION);
2158         return canvas;
2159 }
2160
2161 void
2162 CanvasParser::register_canvas_in_map(Canvas::Handle canvas, String as)
2163 {
2164         get_open_canvas_map()[etl::absolute_path(as)]=canvas;
2165         canvas->signal_deleted().connect(sigc::bind(sigc::ptr_fun(_remove_from_open_canvas_map),canvas.get()));
2166         canvas->signal_file_name_changed().connect(sigc::bind(sigc::ptr_fun(_canvas_file_name_changed),canvas.get()));
2167 }
2168
2169 #ifdef _DEBUG
2170 void
2171 CanvasParser::show_canvas_map(String file, int line, String text)
2172 {
2173         return;
2174         printf("  .-----\n  |  %s:%d %s\n", file.c_str(), line, text.c_str());
2175         std::map<synfig::String, etl::loose_handle<Canvas> > canvas_map(synfig::get_open_canvas_map());
2176         std::map<synfig::String, etl::loose_handle<Canvas> >::iterator iter;
2177         for (iter = canvas_map.begin(); iter != canvas_map.end(); iter++)
2178         {
2179                 synfig::String first(iter->first);
2180                 etl::loose_handle<Canvas> second(iter->second);
2181                 printf("  |    %40s : %lx (%d)\n", first.c_str(), ulong(&*second), second->count());
2182         }
2183         printf("  `-----\n\n");
2184 }
2185 #endif  // _DEBUG
2186
2187 Canvas::Handle
2188 CanvasParser::parse_from_file_as(const String &file_,const String &as_,String &errors)
2189 {
2190         ChangeLocale change_locale(LC_NUMERIC, "C");
2191         String file(unix_to_local_path(file_));
2192         String as(unix_to_local_path(as_));
2193
2194         try
2195         {
2196                 if(get_open_canvas_map().count(etl::absolute_path(as)))
2197                         return get_open_canvas_map()[etl::absolute_path(as)];
2198
2199                 filename=as;
2200                 total_warnings_=0;
2201                 xmlpp::DomParser parser(file);
2202                 if(parser)
2203                 {
2204                         Canvas::Handle canvas(parse_canvas(parser.get_document()->get_root_node(),0,false,as));
2205                         if (!canvas) return canvas;
2206                         register_canvas_in_map(canvas, as);
2207
2208                         const ValueNodeList& value_node_list(canvas->value_node_list());
2209
2210                         again:
2211                         ValueNodeList::const_iterator iter;
2212                         for(iter=value_node_list.begin();iter!=value_node_list.end();++iter)
2213                         {
2214                                 ValueNode::Handle value_node(*iter);
2215                                 if(value_node->is_exported() && value_node->get_id().find("Unnamed")==0)
2216                                 {
2217                                         canvas->remove_value_node(value_node);
2218                                         goto again;
2219                                 }
2220                         }
2221
2222                         return canvas;
2223                 }
2224         }
2225         catch(Exception::BadLinkName) { synfig::error("BadLinkName Thrown"); }
2226         catch(Exception::BadType) { synfig::error("BadType Thrown"); }
2227         catch(Exception::FileNotFound) { synfig::error("FileNotFound Thrown"); }
2228         catch(Exception::IDNotFound) { synfig::error("IDNotFound Thrown"); }
2229         catch(Exception::IDAlreadyExists) { synfig::error("IDAlreadyExists Thrown"); }
2230         catch(xmlpp::internal_error x)
2231         {
2232                 if (!strcmp(x.what(), "Couldn't create parsing context"))
2233                         throw runtime_error(String("  * ") + _("Can't open file") + " \"" + file + "\"");
2234                 throw;
2235         }
2236         catch(const std::exception& ex)
2237         {
2238                 synfig::error("Standard Exception: "+String(ex.what()));
2239                 errors = ex.what();
2240                 return Canvas::Handle();
2241         }
2242         catch(const String& str)
2243         {
2244                 cerr<<str<<endl;
2245                 //      synfig::error(str);
2246                 errors = str;
2247                 return Canvas::Handle();
2248         }
2249         return Canvas::Handle();
2250 }
2251
2252 Canvas::Handle
2253 CanvasParser::parse_as(xmlpp::Element* node,String &errors)
2254 {
2255         ChangeLocale change_locale(LC_NUMERIC, "C");
2256         try
2257         {
2258                 total_warnings_=0;
2259                 if(node)
2260                 {
2261                         Canvas::Handle canvas(parse_canvas(node,0,false,""));
2262                         if (!canvas) return canvas;
2263
2264                         const ValueNodeList& value_node_list(canvas->value_node_list());
2265
2266                         again:
2267                         ValueNodeList::const_iterator iter;
2268                         for(iter=value_node_list.begin();iter!=value_node_list.end();++iter)
2269                         {
2270                                 ValueNode::Handle value_node(*iter);
2271                                 if(value_node->is_exported() && value_node->get_id().find("Unnamed")==0)
2272                                 {
2273                                         canvas->remove_value_node(value_node);
2274                                         goto again;
2275                                 }
2276                         }
2277
2278                         return canvas;
2279                 }
2280         }
2281         catch(Exception::BadLinkName) { synfig::error("BadLinkName Thrown"); }
2282         catch(Exception::BadType) { synfig::error("BadType Thrown"); }
2283         catch(Exception::FileNotFound) { synfig::error("FileNotFound Thrown"); }
2284         catch(Exception::IDNotFound) { synfig::error("IDNotFound Thrown"); }
2285         catch(Exception::IDAlreadyExists) { synfig::error("IDAlreadyExists Thrown"); }
2286         catch(xmlpp::internal_error x)
2287         {
2288                 if (!strcmp(x.what(), "Couldn't create parsing context"))
2289                         throw runtime_error(String("  * ") + _("Can't open file") + " \"" + "\"");
2290                 throw;
2291         }
2292         catch(const std::exception& ex)
2293         {
2294                 synfig::error("Standard Exception: "+String(ex.what()));
2295                 errors = ex.what();
2296                 return Canvas::Handle();
2297         }
2298         catch(const String& str)
2299         {
2300                 cerr<<str<<endl;
2301                 errors = str;
2302                 return Canvas::Handle();
2303         }
2304         return Canvas::Handle();
2305 }
2306 //extern
2307 Canvas::Handle
2308 synfig::open_canvas(xmlpp::Element* node,String &errors,String &warnings){
2309         Canvas::Handle canvas;
2310         CanvasParser parser;
2311         parser.set_allow_errors(true);
2312         try
2313         {
2314                 canvas=parser.parse_as(node,errors);
2315         }
2316         catch (...)
2317         {
2318                 synfig::error("open canvas of a node");
2319                 throw;
2320         }
2321
2322         warnings = parser.get_warnings_text();
2323
2324         if(parser.error_count())
2325         {
2326                 errors = parser.get_errors_text();
2327                 return Canvas::Handle();
2328         }
2329         return canvas;
2330 }
2331
2332