When a .sif file fails to load, display the error messages in the studio GUI, not...
[synfig.git] / synfig-core / trunk / src / synfig / canvas.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file canvas.cpp
3 **      \brief Canvas Class Member Definitions
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007, 2008 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #define SYNFIG_NO_ANGLE
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 "layer.h"
36 #include "canvas.h"
37 #include <cassert>
38 #include "exception.h"
39 #include "time.h"
40 #include "context.h"
41 #include "layer_pastecanvas.h"
42 #include <sigc++/bind.h>
43
44 #endif
45
46 using namespace synfig;
47 using namespace etl;
48 using namespace std;
49
50 namespace synfig { extern Canvas::Handle open_canvas(const String &filename, String &errors); };
51
52 /* === M A C R O S ========================================================= */
53
54 struct _CanvasCounter
55 {
56         static int counter;
57         ~_CanvasCounter()
58         {
59                 if(counter)
60                         synfig::error("%d canvases not yet deleted!",counter);
61         }
62 } _canvas_counter;
63
64 int _CanvasCounter::counter(0);
65
66 /* === G L O B A L S ======================================================= */
67
68 /* === P R O C E D U R E S ================================================= */
69
70 /* === M E T H O D S ======================================================= */
71
72 Canvas::Canvas(const String &id):
73         id_                     (id),
74         version_        (CURRENT_CANVAS_VERSION),
75         cur_time_       (0),
76         is_inline_      (false),
77         is_dirty_       (true),
78         op_flag_        (false)
79 {
80         _CanvasCounter::counter++;
81         clear();
82 }
83
84 void
85 Canvas::on_changed()
86 {
87         is_dirty_=true;
88         Node::on_changed();
89 }
90
91 Canvas::~Canvas()
92 {
93         // we were having a crash where pastecanvas layers were still
94         // refering to a canvas after it had been destroyed;  this code
95         // will stop the pastecanvas layers from refering to the canvas
96         // before the canvas is destroyed
97
98         // the set_sub_canvas(0) ends up deleting the parent-child link,
99         // which deletes the current element from the set we're iterating
100         // through, so we have to make sure we've incremented the iterator
101         // before we mess with the pastecanvas
102         std::set<Node*>::iterator iter = parent_set.begin();
103         while (iter != parent_set.end())
104         {
105                 Layer_PasteCanvas* paste_canvas = dynamic_cast<Layer_PasteCanvas*>(*iter);
106                 iter++;
107                 if(paste_canvas)
108                         paste_canvas->set_sub_canvas(0);
109                 else
110                         warning("destroyed canvas has a parent that is not a pastecanvas - please report if repeatable");
111         }
112
113         //if(is_inline() && parent_) assert(0);
114         _CanvasCounter::counter--;
115         clear();
116         begin_delete();
117 }
118
119 Canvas::iterator
120 Canvas::end()
121 {
122         return CanvasBase::end()-1;
123 }
124
125 Canvas::const_iterator
126 Canvas::end()const
127 {
128         return CanvasBase::end()-1;
129 }
130
131 Canvas::reverse_iterator
132 Canvas::rbegin()
133 {
134         return CanvasBase::rbegin()+1;
135 }
136
137 Canvas::const_reverse_iterator
138 Canvas::rbegin()const
139 {
140         return CanvasBase::rbegin()+1;
141 }
142
143 int
144 Canvas::size()const
145 {
146         return CanvasBase::size()-1;
147 }
148
149 void
150 Canvas::clear()
151 {
152         while(!empty())
153         {
154                 Layer::Handle layer(front());
155                 //if(layer->count()>2)synfig::info("before layer->count()=%d",layer->count());
156
157                 erase(begin());
158                 //if(layer->count()>1)synfig::info("after layer->count()=%d",layer->count());
159         }
160         //CanvasBase::clear();
161
162         // We need to keep a blank handle at the
163         // end of the image list, and acts at
164         // the bottom. Without it, the layers
165         // would just continue going when polled
166         // for a color.
167         CanvasBase::push_back(Layer::Handle());
168
169         changed();
170 }
171
172 bool
173 Canvas::empty()const
174 {
175         return CanvasBase::size()<=1;
176 }
177
178 Layer::Handle &
179 Canvas::back()
180 {
181         return *(CanvasBase::end()-1);
182 }
183
184 const Layer::Handle &
185 Canvas::back()const
186 {
187         return *(CanvasBase::end()-1);
188 }
189
190 Context
191 Canvas::get_context()const
192 {
193         return begin();
194 }
195
196 const ValueNodeList &
197 Canvas::value_node_list()const
198 {
199         if(is_inline() && parent_)
200                 return parent_->value_node_list();
201         return value_node_list_;
202 }
203
204 KeyframeList &
205 Canvas::keyframe_list()
206 {
207         if(is_inline() && parent_)
208                 return parent_->keyframe_list();
209         return keyframe_list_;
210 }
211
212 const KeyframeList &
213 Canvas::keyframe_list()const
214 {
215         if(is_inline() && parent_)
216                 return parent_->keyframe_list();
217         return keyframe_list_;
218 }
219
220 etl::handle<Layer>
221 Canvas::find_layer(const Point &pos)
222 {
223         return get_context().hit_check(pos);
224 }
225
226 static bool
227 valid_id(const String &x)
228 {
229         static const char bad_chars[]=" :#@$^&()*";
230         unsigned int i;
231
232         if(!x.empty() && x[0]>='0' && x[0]<='9')
233                 return false;
234
235         for(i=0;i<sizeof(bad_chars);i++)
236                 if(x.find_first_of(bad_chars[i])!=string::npos)
237                         return false;
238
239         return true;
240 }
241
242 void
243 Canvas::set_id(const String &x)
244 {
245         if(is_inline() && parent_)
246                 throw runtime_error("Inline Canvas cannot have an ID");
247
248         if(!valid_id(x))
249                 throw runtime_error("Invalid ID");
250         id_=x;
251         signal_id_changed_();
252 }
253
254 void
255 Canvas::set_name(const String &x)
256 {
257         name_=x;
258         signal_meta_data_changed()("name");
259         signal_meta_data_changed("name")();
260 }
261
262 void
263 Canvas::set_author(const String &x)
264 {
265         author_=x;
266         signal_meta_data_changed()("author");
267         signal_meta_data_changed("author")();
268 }
269
270 void
271 Canvas::set_description(const String &x)
272 {
273         description_=x;
274         signal_meta_data_changed()("description");
275         signal_meta_data_changed("description")();
276 }
277
278 void
279 Canvas::set_time(Time t)const
280 {
281         if(is_dirty_ || !get_time().is_equal(t))
282         {
283 #if 0
284                 if(is_root())
285                 {
286                         synfig::info("is_dirty_=%d",is_dirty_);
287                         synfig::info("get_time()=%f",(float)get_time());
288                         synfig::info("t=%f",(float)t);
289                 }
290 #endif
291
292                 // ...questionable
293                 const_cast<Canvas&>(*this).cur_time_=t;
294
295                 is_dirty_=false;
296                 get_context().set_time(t);
297         }
298         is_dirty_=false;
299 }
300
301 Canvas::LooseHandle
302 Canvas::get_root()const
303 {
304         return parent_?parent_->get_root().get():const_cast<synfig::Canvas *>(this);
305 }
306
307 int
308 Canvas::get_depth(etl::handle<Layer> layer)const
309 {
310         const_iterator iter;
311         int i(0);
312         for(iter=begin();iter!=end();++iter,i++)
313         {
314                 if(layer==*iter)
315                         return i;
316         }
317         return -1;
318 }
319
320 String
321 Canvas::get_relative_id(etl::loose_handle<const Canvas> x)const
322 {
323         if(x->get_root()==this)
324                 return ":";
325         if(is_inline() && parent_)
326                 return parent_->_get_relative_id(x);
327         return _get_relative_id(x);
328 }
329
330 String
331 Canvas::_get_relative_id(etl::loose_handle<const Canvas> x)const
332 {
333         if(is_inline() && parent_)
334                 return parent_->_get_relative_id(x);
335
336         if(x.get()==this)
337                 return String();
338
339         if(parent()==x.get())
340                 return get_id();
341
342         String id;
343
344         const Canvas* canvas=this;
345
346         for(;!canvas->is_root();canvas=canvas->parent().get())
347                 id=':'+canvas->get_id()+id;
348
349         if(x && get_root()!=x->get_root())
350         {
351                 //String file_name=get_file_name();
352                 //String file_path=x->get_file_path();
353
354                 String file_name;
355                 if(is_absolute_path(get_file_name()))
356                         file_name=etl::relative_path(x->get_file_path(),get_file_name());
357                 else
358                         file_name=get_file_name();
359
360                 // If the path of X is inside of file_name,
361                 // then remove it.
362                 //if(file_name.size()>file_path.size())
363                 //      if(file_path==String(file_name,0,file_path.size()))
364                 //              file_name.erase(0,file_path.size()+1);
365
366                 id=file_name+'#'+id;
367         }
368
369         return id;
370 }
371
372 ValueNode::Handle
373 Canvas::find_value_node(const String &id)
374 {
375         return
376                 ValueNode::Handle::cast_const(
377                         const_cast<const Canvas*>(this)->find_value_node(id)
378                 );
379 }
380
381 ValueNode::ConstHandle
382 Canvas::find_value_node(const String &id)const
383 {
384         if(is_inline() && parent_)
385                 return parent_->find_value_node(id);
386
387         if(id.empty())
388                 throw Exception::IDNotFound("Empty ID");
389
390         // If we do not have any resolution, then we assume that the
391         // request is for this immediate canvas
392         if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
393                 return value_node_list_.find(id);
394
395         String canvas_id(id,0,id.rfind(':'));
396         String value_node_id(id,id.rfind(':')+1);
397         if(canvas_id.empty())
398                 canvas_id=':';
399         //synfig::warning("constfind:value_node_id: "+value_node_id);
400         //synfig::warning("constfind:canvas_id: "+canvas_id);
401
402         return find_canvas(canvas_id)->value_node_list_.find(value_node_id);
403 }
404
405 ValueNode::Handle
406 Canvas::surefind_value_node(const String &id)
407 {
408         if(is_inline() && parent_)
409                 return parent_->surefind_value_node(id);
410
411         if(id.empty())
412                 throw Exception::IDNotFound("Empty ID");
413
414         // If we do not have any resolution, then we assume that the
415         // request is for this immediate canvas
416         if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
417                 return value_node_list_.surefind(id);
418
419         String canvas_id(id,0,id.rfind(':'));
420         String value_node_id(id,id.rfind(':')+1);
421         if(canvas_id.empty())
422                 canvas_id=':';
423
424         return surefind_canvas(canvas_id)->value_node_list_.surefind(value_node_id);
425 }
426
427 void
428 Canvas::add_value_node(ValueNode::Handle x, const String &id)
429 {
430         if(is_inline() && parent_)
431                 return parent_->add_value_node(x,id);
432 //              throw runtime_error("You cannot add a ValueNode to an inline Canvas");
433
434         if(x->is_exported())
435                 throw runtime_error("ValueNode is already exported");
436
437         if(id.empty())
438                 throw Exception::BadLinkName("Empty ID");
439
440         if(id.find_first_of(':',0)!=string::npos)
441                 throw Exception::BadLinkName("Bad character");
442
443         try
444         {
445                 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
446                         throw Exception::IDNotFound("add_value_node()");
447
448                 throw Exception::IDAlreadyExists(id);
449         }
450         catch(Exception::IDNotFound)
451         {
452                 x->set_id(id);
453
454                 x->set_parent_canvas(this);
455
456                 if(!value_node_list_.add(x))
457                 {
458                         synfig::error("Unable to add ValueNode");
459                         throw std::runtime_error("Unable to add ValueNode");
460                 }
461
462                 return;
463         }
464 }
465
466 /*
467 void
468 Canvas::rename_value_node(ValueNode::Handle x, const String &id)
469 {
470         if(id.empty())
471                 throw Exception::BadLinkName("Empty ID");
472
473         if(id.find_first_of(": ",0)!=string::npos)
474                 throw Exception::BadLinkName("Bad character");
475
476         try
477         {
478                 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
479                         throw Exception::IDNotFound("rename_value_node");
480                 throw Exception::IDAlreadyExists(id);
481         }
482         catch(Exception::IDNotFound)
483         {
484                 x->set_id(id);
485
486                 return;
487         }
488 }
489 */
490
491 void
492 Canvas::remove_value_node(ValueNode::Handle x)
493 {
494         if(is_inline() && parent_)
495                 return parent_->remove_value_node(x);
496 //              throw Exception::IDNotFound("Canvas::remove_value_node() was called from an inline canvas");
497
498         if(!x)
499                 throw Exception::IDNotFound("Canvas::remove_value_node() was passed empty handle");
500
501         if(!value_node_list_.erase(x))
502                 throw Exception::IDNotFound("Canvas::remove_value_node(): ValueNode was not found inside of this canvas");
503
504         //x->set_parent_canvas(0);
505
506         x->set_id("");
507 }
508
509 etl::handle<Canvas>
510 Canvas::surefind_canvas(const String &id)
511 {
512         if(is_inline() && parent_)
513                 return parent_->surefind_canvas(id);
514
515         if(id.empty())
516                 return this;
517
518         // If the ID contains a "#" character, then a filename is
519         // expected on the left side.
520         if(id.find_first_of('#')!=string::npos)
521         {
522                 // If '#' is the first character, remove it
523                 // and attempt to parse the ID again.
524                 if(id[0]=='#')
525                         return surefind_canvas(String(id,1));
526
527                 //! \todo This needs a lot more optimization
528                 String file_name(id,0,id.find_first_of('#'));
529                 String external_id(id,id.find_first_of('#')+1);
530
531                 file_name=unix_to_local_path(file_name);
532
533                 Canvas::Handle external_canvas;
534
535                 // If the composition is already open, then use it.
536                 if(externals_.count(file_name))
537                         external_canvas=externals_[file_name];
538                 else
539                 {
540                         String errors;
541                         if(is_absolute_path(file_name))
542                                 external_canvas=open_canvas(file_name, errors);
543                         else
544                                 external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name, errors);
545
546                         if(!external_canvas)
547                                 throw Exception::FileNotFound(file_name);
548                         externals_[file_name]=external_canvas;
549                 }
550
551                 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id));
552         }
553
554         // If we do not have any resolution, then we assume that the
555         // request is for this immediate canvas
556         if(id.find_first_of(':')==string::npos)
557         {
558                 Children::iterator iter;
559
560                 // Search for the image in the image list,
561                 // and return it if it is found
562                 for(iter=children().begin();iter!=children().end();iter++)
563                         if(id==(*iter)->get_id())
564                                 return *iter;
565
566                 // Create a new canvas and return it
567                 //synfig::warning("Implicitly creating canvas named "+id);
568                 return new_child_canvas(id);
569         }
570
571         // If the first character is the separator, then
572         // this references the root canvas.
573         if(id[0]==':')
574                 return get_root()->surefind_canvas(string(id,1));
575
576         // Now we know that the requested Canvas is in a child
577         // of this canvas. We have to find that canvas and
578         // call "find_canvas" on it, and return the result.
579
580         String canvas_name=string(id,0,id.find_first_of(':'));
581
582         Canvas::Handle child_canvas=surefind_canvas(canvas_name);
583
584         return child_canvas->surefind_canvas(string(id,id.find_first_of(':')+1));
585 }
586
587 Canvas::Handle
588 Canvas::find_canvas(const String &id)
589 {
590         return
591                 Canvas::Handle::cast_const(
592                         const_cast<const Canvas*>(this)->find_canvas(id)
593                 );
594 }
595
596 Canvas::ConstHandle
597 Canvas::find_canvas(const String &id)const
598 {
599         if(is_inline() && parent_)return parent_->find_canvas(id);
600
601         if(id.empty())
602                 return this;
603
604         // If the ID contains a "#" character, then a filename is
605         // expected on the left side.
606         if(id.find_first_of('#')!=string::npos)
607         {
608                 // If '#' is the first character, remove it
609                 // and attempt to parse the ID again.
610                 if(id[0]=='#')
611                         return find_canvas(String(id,1));
612
613                 //! \todo This needs a lot more optimization
614                 String file_name(id,0,id.find_first_of('#'));
615                 String external_id(id,id.find_first_of('#')+1);
616
617                 file_name=unix_to_local_path(file_name);
618
619                 Canvas::Handle external_canvas;
620
621                 // If the composition is already open, then use it.
622                 if(externals_.count(file_name))
623                         external_canvas=externals_[file_name];
624                 else
625                 {
626                         String errors;
627                         if(is_absolute_path(file_name))
628                                 external_canvas=open_canvas(file_name, errors);
629                         else
630                                 external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name, errors);
631
632                         if(!external_canvas)
633                                 throw Exception::FileNotFound(file_name);
634                         externals_[file_name]=external_canvas;
635                 }
636
637                 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id));
638         }
639
640         // If we do not have any resolution, then we assume that the
641         // request is for this immediate canvas
642         if(id.find_first_of(':')==string::npos)
643         {
644                 Children::const_iterator iter;
645
646                 // Search for the image in the image list,
647                 // and return it if it is found
648                 for(iter=children().begin();iter!=children().end();iter++)
649                         if(id==(*iter)->get_id())
650                                 return *iter;
651
652                 throw Exception::IDNotFound("Child Canvas in Parent Canvas: (child)"+id);
653         }
654
655         // If the first character is the separator, then
656         // this references the root canvas.
657         if(id.find_first_of(':')==0)
658                 return get_root()->find_canvas(string(id,1));
659
660         // Now we know that the requested Canvas is in a child
661         // of this canvas. We have to find that canvas and
662         // call "find_canvas" on it, and return the result.
663
664         String canvas_name=string(id,0,id.find_first_of(':'));
665
666         Canvas::ConstHandle child_canvas=find_canvas(canvas_name);
667
668         return child_canvas->find_canvas(string(id,id.find_first_of(':')+1));
669 }
670
671 Canvas::Handle
672 Canvas::create()
673 {
674         return new Canvas("Untitled");
675 }
676
677 void
678 Canvas::push_back(etl::handle<Layer> x)
679 {
680 //      int i(x->count());
681         insert(end(),x);
682         //if(x->count()!=i+1)synfig::info("push_back before %d, after %d",i,x->count());
683 }
684
685 void
686 Canvas::push_front(etl::handle<Layer> x)
687 {
688 //      int i(x->count());
689         insert(begin(),x);
690         //if(x->count()!=i+1)synfig::error("push_front before %d, after %d",i,x->count());
691 }
692
693 void
694 Canvas::insert(iterator iter,etl::handle<Layer> x)
695 {
696 //      int i(x->count());
697         CanvasBase::insert(iter,x);
698
699         /*if(x->count()!=i+1)
700         {
701                 synfig::error(__FILE__":%d: Canvas::insert(): ***FAILURE*** before %d, after %d",__LINE__,i,x->count());
702                 return;
703                 //throw runtime_error("Canvas Insertion Failed");
704         }*/
705
706         x->set_canvas(this);
707
708         add_child(x.get());
709
710         LooseHandle correct_canvas(this);
711         //while(correct_canvas->is_inline())correct_canvas=correct_canvas->parent();
712         Layer::LooseHandle loose_layer(x);
713
714         add_connection(loose_layer,
715                                    sigc::connection::connection(
716                                            x->signal_added_to_group().connect(
717                                                    sigc::bind(
718                                                            sigc::mem_fun(
719                                                                    *correct_canvas,
720                                                                    &Canvas::add_group_pair),
721                                                            loose_layer))));
722         add_connection(loose_layer,
723                                    sigc::connection::connection(
724                                            x->signal_removed_from_group().connect(
725                                                    sigc::bind(
726                                                            sigc::mem_fun(
727                                                                    *correct_canvas,
728                                                                    &Canvas::remove_group_pair),
729                                                            loose_layer))));
730
731         if(!x->get_group().empty())
732                 add_group_pair(x->get_group(),x);
733
734         changed();
735 }
736
737 void
738 Canvas::push_back_simple(etl::handle<Layer> x)
739 {
740         CanvasBase::insert(end(),x);
741         changed();
742 }
743
744 void
745 Canvas::erase(iterator iter)
746 {
747         if(!(*iter)->get_group().empty())
748                 remove_group_pair((*iter)->get_group(),(*iter));
749
750         // HACK: We really shouldn't be wiping
751         // out these signals entirely. We should
752         // only be removing the specific connections
753         // that we made. At the moment, I'm too
754         // lazy to add the code to keep track
755         // of those connections, and no one else
756         // is using these signals, so I'll just
757         // leave these next two lines like they
758         // are for now - darco 07-30-2004
759
760         // so don't wipe them out entirely
761         // - dooglus 09-21-2007
762         disconnect_connections(*iter);
763
764         if(!op_flag_)remove_child(iter->get());
765
766         CanvasBase::erase(iter);
767         if(!op_flag_)changed();
768 }
769
770 Canvas::Handle
771 Canvas::clone(const GUID& deriv_guid)const
772 {
773         synfig::String name;
774         if(is_inline())
775                 name="inline";
776         else
777         {
778                 name=get_id()+"_CLONE";
779
780                 throw runtime_error("Cloning of non-inline canvases is not yet supported");
781         }
782
783         Handle canvas(new Canvas(name));
784
785         if(is_inline())
786         {
787                 canvas->is_inline_=true;
788                 // \todo this was setting parent_=0 - is there a reason for that?
789                 // this was causing bug 1838132, where cloning an inline canvas that contains an imported image fails
790                 // it was failing to ascertain the absolute pathname of the imported image, since it needs the pathname
791                 // of the canvas to get that, which is stored in the parent canvas
792                 canvas->parent_=parent();
793                 canvas->rend_desc() = rend_desc();
794                 //canvas->set_inline(parent());
795         }
796
797         canvas->set_guid(get_guid()^deriv_guid);
798
799         const_iterator iter;
800         for(iter=begin();iter!=end();++iter)
801         {
802                 Layer::Handle layer((*iter)->clone(deriv_guid));
803                 if(layer)
804                 {
805                         assert(layer.count()==1);
806                         int presize(size());
807                         canvas->push_back(layer);
808                         if(!(layer.count()>1))
809                         {
810                                 synfig::error("Canvas::clone(): Cloned layer insertion failure!");
811                                 synfig::error("Canvas::clone(): \tlayer.count()=%d",layer.count());
812                                 synfig::error("Canvas::clone(): \tlayer->get_name()=%s",layer->get_name().c_str());
813                                 synfig::error("Canvas::clone(): \tbefore size()=%d",presize);
814                                 synfig::error("Canvas::clone(): \tafter size()=%d",size());
815                         }
816                         assert(layer.count()>1);
817                 }
818                 else
819                 {
820                         synfig::error("Unable to clone layer");
821                 }
822         }
823
824         canvas->signal_group_pair_removed().clear();
825         canvas->signal_group_pair_added().clear();
826
827         return canvas;
828 }
829
830 void
831 Canvas::set_inline(LooseHandle parent)
832 {
833         if(is_inline_ && parent_)
834         {
835
836         }
837
838         id_="inline";
839         is_inline_=true;
840         parent_=parent;
841
842         // Have the parent inherit all of the group stuff
843
844         std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
845
846         for(iter=group_db_.begin();iter!=group_db_.end();++iter)
847         {
848                 parent->group_db_[iter->first].insert(iter->second.begin(),iter->second.end());
849         }
850
851         rend_desc()=parent->rend_desc();
852 }
853
854 Canvas::Handle
855 Canvas::create_inline(Handle parent)
856 {
857         assert(parent);
858         //if(parent->is_inline())
859         //      return create_inline(parent->parent());
860
861         Handle canvas(new Canvas("inline"));
862         canvas->set_inline(parent);
863         return canvas;
864 }
865
866 Canvas::Handle
867 Canvas::new_child_canvas()
868 {
869         if(is_inline() && parent_)
870                 return parent_->new_child_canvas();
871 //              runtime_error("You cannot create a child Canvas in an inline Canvas");
872
873         // Create a new canvas
874         children().push_back(create());
875         Canvas::Handle canvas(children().back());
876
877         canvas->parent_=this;
878
879         canvas->rend_desc()=rend_desc();
880
881         return canvas;
882 }
883
884 Canvas::Handle
885 Canvas::new_child_canvas(const String &id)
886 {
887         if(is_inline() && parent_)
888                 return parent_->new_child_canvas(id);
889 //              runtime_error("You cannot create a child Canvas in an inline Canvas");
890
891         // Create a new canvas
892         children().push_back(create());
893         Canvas::Handle canvas(children().back());
894
895         canvas->set_id(id);
896         canvas->parent_=this;
897         canvas->rend_desc()=rend_desc();
898
899         return canvas;
900 }
901
902 Canvas::Handle
903 Canvas::add_child_canvas(Canvas::Handle child_canvas, const synfig::String& id)
904 {
905         if(is_inline() && parent_)
906                 return parent_->add_child_canvas(child_canvas,id);
907
908         if(child_canvas->parent() && !child_canvas->is_inline())
909                 throw std::runtime_error("Cannot add child canvas because it belongs to someone else!");
910
911         if(!valid_id(id))
912                 throw runtime_error("Invalid ID");
913
914         try
915         {
916                 find_canvas(id);
917                 throw Exception::IDAlreadyExists(id);
918         }
919         catch(Exception::IDNotFound)
920         {
921                 if(child_canvas->is_inline())
922                         child_canvas->is_inline_=false;
923                 child_canvas->id_=id;
924                 children().push_back(child_canvas);
925                 child_canvas->parent_=this;
926         }
927
928         return child_canvas;
929 }
930
931 void
932 Canvas::remove_child_canvas(Canvas::Handle child_canvas)
933 {
934         if(is_inline() && parent_)
935                 return parent_->remove_child_canvas(child_canvas);
936
937         if(child_canvas->parent_!=this)
938                 throw runtime_error("Given child does not belong to me");
939
940         if(find(children().begin(),children().end(),child_canvas)==children().end())
941                 throw Exception::IDNotFound(child_canvas->get_id());
942
943         children().remove(child_canvas);
944
945         child_canvas->parent_=0;
946 }
947
948 void
949 Canvas::set_file_name(const String &file_name)
950 {
951         if(parent())
952                 parent()->set_file_name(file_name);
953         else
954         {
955                 file_name_=file_name;
956                 signal_file_name_changed_();
957         }
958 }
959
960 sigc::signal<void>&
961 Canvas::signal_file_name_changed()
962 {
963         if(parent())
964                 return signal_file_name_changed();
965         else
966                 return signal_file_name_changed_;
967 }
968
969 String
970 Canvas::get_file_name()const
971 {
972         if(parent())
973                 return parent()->get_file_name();
974         return file_name_;
975 }
976
977 String
978 Canvas::get_file_path()const
979 {
980         if(parent())
981                 return parent()->get_file_path();
982         return dirname(file_name_);
983 }
984
985 String
986 Canvas::get_meta_data(const String& key)const
987 {
988         if(!meta_data_.count(key))
989                 return String();
990         return meta_data_.find(key)->second;
991 }
992
993 void
994 Canvas::set_meta_data(const String& key, const String& data)
995 {
996         if(meta_data_[key]!=data)
997         {
998                 meta_data_[key]=data;
999                 signal_meta_data_changed()(key);
1000                 signal_meta_data_changed(key)();
1001         }
1002 }
1003
1004 void
1005 Canvas::erase_meta_data(const String& key)
1006 {
1007         if(meta_data_.count(key))
1008         {
1009                 meta_data_.erase(key);
1010                 signal_meta_data_changed()(key);
1011                 signal_meta_data_changed(key)();
1012         }
1013 }
1014
1015 std::list<String>
1016 Canvas::get_meta_data_keys()const
1017 {
1018         std::list<String> ret;
1019
1020         std::map<String,String>::const_iterator iter;
1021
1022         for(iter=meta_data_.begin();!(iter==meta_data_.end());++iter)
1023                 ret.push_back(iter->first);
1024
1025         return ret;
1026 }
1027
1028 /* note - the "Motion Blur" and "Duplicate" layers need the dynamic
1029                   parameters of any PasteCanvas layers they loop over to be
1030                   maintained.  When the variables in the following function
1031                   refer to "motion blur", they mean either of these two
1032                   layers. */
1033 void
1034 synfig::optimize_layers(Time time, Context context, Canvas::Handle op_canvas, bool seen_motion_blur_in_parent)
1035 {
1036         Context iter;
1037
1038         std::vector< std::pair<float,Layer::Handle> > sort_list;
1039         int i, motion_blur_i=0; // motion_blur_i is for resolving which layer comes first in the event of a z_depth tie
1040         float motion_blur_z_depth=0; // the z_depth of the least deep motion blur layer in this context
1041         bool seen_motion_blur_locally = false;
1042         bool motion_blurred; // the final result - is this layer blurred or not?
1043
1044         // If the parent didn't cause us to already be motion blurred,
1045         // check whether there's a motion blur in this context,
1046         // and if so, calculate its z_depth.
1047         if (!seen_motion_blur_in_parent)
1048                 for(iter=context,i=0;*iter;iter++,i++)
1049                 {
1050                         Layer::Handle layer=*iter;
1051
1052                         // If the layer isn't active, don't worry about it
1053                         if(!layer->active())
1054                                 continue;
1055
1056                         // Any layer with an amount of zero is implicitly disabled.
1057                         ValueBase value(layer->get_param("amount"));
1058                         if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1059                                 continue;
1060
1061                         if(layer->get_name()=="MotionBlur" || layer->get_name()=="duplicate")
1062                         {
1063                                 float z_depth(layer->get_z_depth()*1.0001+i);
1064
1065                                 // If we've seen a motion blur before in this context...
1066                                 if (seen_motion_blur_locally)
1067                                 {
1068                                         // ... then we're only interested in this one if it's less deep...
1069                                         if (z_depth < motion_blur_z_depth)
1070                                         {
1071                                                 motion_blur_z_depth = z_depth;
1072                                                 motion_blur_i = i;
1073                                         }
1074                                 }
1075                                 // ... otherwise we're always interested in it.
1076                                 else
1077                                 {
1078                                         motion_blur_z_depth = z_depth;
1079                                         motion_blur_i = i;
1080                                         seen_motion_blur_locally = true;
1081                                 }
1082                         }
1083                 }
1084
1085         // Go ahead and start romping through the canvas to paste
1086         for(iter=context,i=0;*iter;iter++,i++)
1087         {
1088                 Layer::Handle layer=*iter;
1089                 float z_depth(layer->get_z_depth()*1.0001+i);
1090
1091                 // If the layer isn't active, don't worry about it
1092                 if(!layer->active())
1093                         continue;
1094
1095                 // Any layer with an amount of zero is implicitly disabled.
1096                 ValueBase value(layer->get_param("amount"));
1097                 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1098                         continue;
1099
1100                 // note: this used to include "&& paste_canvas->get_time_offset()==0", but then
1101                 //               time-shifted layers weren't being sorted by z-depth (bug #1806852)
1102                 if(layer->get_name()=="PasteCanvas")
1103                 {
1104                         Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1105
1106                         // we need to blur the sub canvas if:
1107                         // our parent is blurred,
1108                         // or the child is lower than a local blur,
1109                         // or the child is at the same z_depth as a local blur, but later in the context
1110
1111 #if 0 // DEBUG
1112                         if (seen_motion_blur_in_parent)                                 synfig::info("seen BLUR in parent\n");
1113                         else if (seen_motion_blur_locally)
1114                                 if (z_depth > motion_blur_z_depth)                      synfig::info("paste is deeper than BLUR\n");
1115                                 else if (z_depth == motion_blur_z_depth) {      synfig::info("paste is same depth as BLUR\n");
1116                                         if (i > motion_blur_i)                                  synfig::info("paste is physically deeper than BLUR\n");
1117                                         else                                                                    synfig::info("paste is less physically deep than BLUR\n");
1118                                 } else                                                                          synfig::info("paste is less deep than BLUR\n");
1119                         else                                                                                    synfig::info("no BLUR at all\n");
1120 #endif  // DEBUG
1121
1122                         motion_blurred = (seen_motion_blur_in_parent ||
1123                                                           (seen_motion_blur_locally &&
1124                                                            (z_depth > motion_blur_z_depth ||
1125                                                                 (z_depth == motion_blur_z_depth && i > motion_blur_i))));
1126
1127                         Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1128                         Canvas::Handle paste_sub_canvas = paste_canvas->get_sub_canvas();
1129                         if(paste_sub_canvas)
1130                                 optimize_layers(time, paste_sub_canvas->get_context(),sub_canvas,motion_blurred);
1131
1132 // \todo: uncommenting the following breaks the rendering of at least examples/backdrop.sifz quite severely
1133 // #define SYNFIG_OPTIMIZE_PASTE_CANVAS
1134 #ifdef SYNFIG_OPTIMIZE_PASTE_CANVAS
1135                         Canvas::iterator sub_iter;
1136
1137                         // Determine if we can just remove the paste canvas altogether
1138                         if (paste_canvas->get_blend_method()    == Color::BLEND_COMPOSITE       &&
1139                                 paste_canvas->get_amount()                      == 1.0f                                         &&
1140                                 paste_canvas->get_zoom()                        == 0                                            &&
1141                                 paste_canvas->get_time_offset()         == 0                                            &&
1142                                 paste_canvas->get_origin()                      == Point(0,0)                           )
1143                                 try {
1144                                         for(sub_iter=sub_canvas->begin();sub_iter!=sub_canvas->end();++sub_iter)
1145                                         {
1146                                                 Layer* layer=sub_iter->get();
1147
1148                                                 // any layers that deform end up breaking things
1149                                                 // so do things the old way if we run into anything like this
1150                                                 if(!dynamic_cast<Layer_NoDeform*>(layer))
1151                                                         throw int();
1152
1153                                                 ValueBase value(layer->get_param("blend_method"));
1154                                                 if(value.get_type()!=ValueBase::TYPE_INTEGER || value.get(int())!=(int)Color::BLEND_COMPOSITE)
1155                                                         throw int();
1156                                         }
1157
1158                                         // It has turned out that we don't need a paste canvas
1159                                         // layer, so just go ahead and add all the layers onto
1160                                         // the current stack and be done with it
1161                                         while(sub_canvas->size())
1162                                         {
1163                                                 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,sub_canvas->front()));
1164                                                 //op_canvas->push_back_simple(sub_canvas->front());
1165                                                 sub_canvas->pop_front();
1166                                         }
1167                                         continue;
1168                                 }
1169                                 catch(int)
1170                                 { }
1171 #endif  // SYNFIG_OPTIMIZE_PASTE_CANVAS
1172
1173                         Layer::Handle new_layer(Layer::create("PasteCanvas"));
1174                         dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(false);
1175                         if (motion_blurred)
1176                         {
1177                                 Layer::DynamicParamList dynamic_param_list(paste_canvas->dynamic_param_list());
1178                                 for(Layer::DynamicParamList::const_iterator iter(dynamic_param_list.begin()); iter != dynamic_param_list.end(); ++iter)
1179                                         new_layer->connect_dynamic_param(iter->first, iter->second);
1180                         }
1181                         Layer::ParamList param_list(paste_canvas->get_param_list());
1182                         //param_list.erase("canvas");
1183                         new_layer->set_param_list(param_list);
1184                         dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_sub_canvas(sub_canvas);
1185                         dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(true);
1186                         layer=new_layer;
1187                 }
1188                 else                                    // not a PasteCanvas - does it use blend method 'Straight'?
1189                 {
1190                         /* when we use the 'straight' blend method, every pixel on the layer affects the layers underneath,
1191                          * not just the non-transparent pixels; the following workarea wraps non-pastecanvas layers in a
1192                          * new pastecanvas to ensure that the straight blend affects the full plane, not just the area
1193                          * within the layer's bounding box
1194                          */
1195
1196                         // \todo: this code probably needs modification to work properly with motionblur and duplicate
1197                         etl::handle<Layer_Composite> composite = etl::handle<Layer_Composite>::cast_dynamic(layer);
1198
1199                         /* some layers (such as circle) don't touch pixels that aren't
1200                          * part of the circle, so they don't get blended correctly when
1201                          * using a straight blend.  so we encapsulate the circle, and the
1202                          * encapsulation layer takes care of the transparent pixels
1203                          * for us.  if we do that for all layers, however, then the
1204                          * distortion layers no longer work, since they have no
1205                          * context to work on.  the Layer::reads_context() method
1206                          * returns true for layers which need to be able to see
1207                          * their context.  we can't encapsulate those.
1208                          */
1209                         if (composite &&
1210                                 Color::is_straight(composite->get_blend_method()) &&
1211                                 !composite->reads_context())
1212                         {
1213                                 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1214                                 sub_canvas->push_back(composite = composite->clone());
1215                                 layer = Layer::create("PasteCanvas");
1216                                 composite->set_description(strprintf("Wrapped clone of '%s'", composite->get_non_empty_description().c_str()));
1217                                 layer->set_description(strprintf("PasteCanvas wrapper for '%s'", composite->get_non_empty_description().c_str()));
1218                                 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1219                                 paste_canvas->set_blend_method(composite->get_blend_method());
1220                                 paste_canvas->set_amount(composite->get_amount());
1221                                 sub_canvas->set_time(time); // region and outline don't calculate their bounding rects until their time is set
1222                                 composite->set_blend_method(Color::BLEND_STRAIGHT); // do this before calling set_sub_canvas(), but after set_time()
1223                                 composite->set_amount(1.0f); // after set_time()
1224                                 paste_canvas->set_sub_canvas(sub_canvas);
1225                         }
1226                 }
1227
1228                 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,layer));
1229                 //op_canvas->push_back_simple(layer);
1230         }
1231
1232         //sort_list.sort();
1233         stable_sort(sort_list.begin(),sort_list.end());
1234         std::vector< std::pair<float,Layer::Handle> >::iterator iter2;
1235         for(iter2=sort_list.begin();iter2!=sort_list.end();++iter2)
1236                 op_canvas->push_back_simple(iter2->second);
1237         op_canvas->op_flag_=true;
1238 }
1239
1240 void
1241 Canvas::get_times_vfunc(Node::time_set &set) const
1242 {
1243         const_iterator  i = begin(),
1244                                 iend = end();
1245
1246         for(; i != iend; ++i)
1247         {
1248                 const Node::time_set &tset = (*i)->get_times();
1249                 set.insert(tset.begin(),tset.end());
1250         }
1251 }
1252
1253 std::set<etl::handle<Layer> >
1254 Canvas::get_layers_in_group(const String&group)
1255 {
1256         if(is_inline() && parent_)
1257                 return parent_->get_layers_in_group(group);
1258
1259         if(group_db_.count(group)==0)
1260                 return std::set<etl::handle<Layer> >();
1261         return group_db_.find(group)->second;
1262 }
1263
1264 std::set<String>
1265 Canvas::get_groups()const
1266 {
1267         if(is_inline() && parent_)
1268                 return parent_->get_groups();
1269
1270         std::set<String> ret;
1271         std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
1272         for(iter=group_db_.begin();iter!=group_db_.end();++iter)
1273                 ret.insert(iter->first);
1274         return ret;
1275 }
1276
1277 int
1278 Canvas::get_group_count()const
1279 {
1280         if(is_inline() && parent_)
1281                 return parent_->get_group_count();
1282
1283         return group_db_.size();
1284 }
1285
1286 void
1287 Canvas::add_group_pair(String group, etl::handle<Layer> layer)
1288 {
1289         group_db_[group].insert(layer);
1290         if(group_db_[group].size()==1)
1291                 signal_group_added()(group);
1292         else
1293                 signal_group_changed()(group);
1294
1295         signal_group_pair_added()(group,layer);
1296
1297         if(is_inline()  && parent_)
1298                 return parent_->add_group_pair(group,layer);
1299 }
1300
1301 void
1302 Canvas::remove_group_pair(String group, etl::handle<Layer> layer)
1303 {
1304         group_db_[group].erase(layer);
1305
1306         signal_group_pair_removed()(group,layer);
1307
1308         if(group_db_[group].empty())
1309         {
1310                 group_db_.erase(group);
1311                 signal_group_removed()(group);
1312         }
1313         else
1314                 signal_group_changed()(group);
1315
1316         if(is_inline() && parent_)
1317                 return parent_->remove_group_pair(group,layer);
1318 }
1319
1320 void
1321 Canvas::add_connection(etl::loose_handle<Layer> layer, sigc::connection connection)
1322 {
1323         connections_[layer].push_back(connection);
1324 }
1325
1326 void
1327 Canvas::disconnect_connections(etl::loose_handle<Layer> layer)
1328 {
1329         std::vector<sigc::connection>::iterator iter;
1330         for(iter=connections_[layer].begin();iter!=connections_[layer].end();++iter)
1331                 iter->disconnect();
1332         connections_[layer].clear();
1333 }
1334
1335 void
1336 Canvas::rename_group(const String&old_name,const String&new_name)
1337 {
1338         if(is_inline() && parent_)
1339                 return parent_->rename_group(old_name,new_name);
1340
1341         {
1342                 std::map<String,std::set<etl::handle<Layer> > >::iterator iter;
1343                 iter=group_db_.find(old_name);
1344                 if(iter!=group_db_.end())
1345                 for(++iter;iter!=group_db_.end() && iter->first.find(old_name)==0;iter=group_db_.find(old_name),++iter)
1346                 {
1347                         String name(iter->first,old_name.size(),String::npos);
1348                         name=new_name+name;
1349                         rename_group(iter->first,name);
1350                 }
1351         }
1352
1353         std::set<etl::handle<Layer> > layers(get_layers_in_group(old_name));
1354         std::set<etl::handle<Layer> >::iterator iter;
1355
1356         for(iter=layers.begin();iter!=layers.end();++iter)
1357         {
1358                 (*iter)->remove_from_group(old_name);
1359                 (*iter)->add_to_group(new_name);
1360         }
1361 }