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