Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-core / tags / 0.61.09 / 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); };
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                         if(is_absolute_path(file_name))
541                                 external_canvas=open_canvas(file_name);
542                         else
543                                 external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name);
544
545                         if(!external_canvas)
546                                 throw Exception::FileNotFound(file_name);
547                         externals_[file_name]=external_canvas;
548                 }
549
550                 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id));
551         }
552
553         // If we do not have any resolution, then we assume that the
554         // request is for this immediate canvas
555         if(id.find_first_of(':')==string::npos)
556         {
557                 Children::iterator iter;
558
559                 // Search for the image in the image list,
560                 // and return it if it is found
561                 for(iter=children().begin();iter!=children().end();iter++)
562                         if(id==(*iter)->get_id())
563                                 return *iter;
564
565                 // Create a new canvas and return it
566                 //synfig::warning("Implicitly creating canvas named "+id);
567                 return new_child_canvas(id);
568         }
569
570         // If the first character is the separator, then
571         // this references the root canvas.
572         if(id[0]==':')
573                 return get_root()->surefind_canvas(string(id,1));
574
575         // Now we know that the requested Canvas is in a child
576         // of this canvas. We have to find that canvas and
577         // call "find_canvas" on it, and return the result.
578
579         String canvas_name=string(id,0,id.find_first_of(':'));
580
581         Canvas::Handle child_canvas=surefind_canvas(canvas_name);
582
583         return child_canvas->surefind_canvas(string(id,id.find_first_of(':')+1));
584 }
585
586 Canvas::Handle
587 Canvas::find_canvas(const String &id)
588 {
589         return
590                 Canvas::Handle::cast_const(
591                         const_cast<const Canvas*>(this)->find_canvas(id)
592                 );
593 }
594
595 Canvas::ConstHandle
596 Canvas::find_canvas(const String &id)const
597 {
598         if(is_inline() && parent_)return parent_->find_canvas(id);
599
600         if(id.empty())
601                 return this;
602
603         // If the ID contains a "#" character, then a filename is
604         // expected on the left side.
605         if(id.find_first_of('#')!=string::npos)
606         {
607                 // If '#' is the first character, remove it
608                 // and attempt to parse the ID again.
609                 if(id[0]=='#')
610                         return find_canvas(String(id,1));
611
612                 //! \todo This needs a lot more optimization
613                 String file_name(id,0,id.find_first_of('#'));
614                 String external_id(id,id.find_first_of('#')+1);
615
616                 file_name=unix_to_local_path(file_name);
617
618                 Canvas::Handle external_canvas;
619
620                 // If the composition is already open, then use it.
621                 if(externals_.count(file_name))
622                         external_canvas=externals_[file_name];
623                 else
624                 {
625                         if(is_absolute_path(file_name))
626                                 external_canvas=open_canvas(file_name);
627                         else
628                                 external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name);
629
630                         if(!external_canvas)
631                                 throw Exception::FileNotFound(file_name);
632                         externals_[file_name]=external_canvas;
633                 }
634
635                 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id));
636         }
637
638         // If we do not have any resolution, then we assume that the
639         // request is for this immediate canvas
640         if(id.find_first_of(':')==string::npos)
641         {
642                 Children::const_iterator iter;
643
644                 // Search for the image in the image list,
645                 // and return it if it is found
646                 for(iter=children().begin();iter!=children().end();iter++)
647                         if(id==(*iter)->get_id())
648                                 return *iter;
649
650                 throw Exception::IDNotFound("Child Canvas in Parent Canvas: (child)"+id);
651         }
652
653         // If the first character is the separator, then
654         // this references the root canvas.
655         if(id.find_first_of(':')==0)
656                 return get_root()->find_canvas(string(id,1));
657
658         // Now we know that the requested Canvas is in a child
659         // of this canvas. We have to find that canvas and
660         // call "find_canvas" on it, and return the result.
661
662         String canvas_name=string(id,0,id.find_first_of(':'));
663
664         Canvas::ConstHandle child_canvas=find_canvas(canvas_name);
665
666         return child_canvas->find_canvas(string(id,id.find_first_of(':')+1));
667 }
668
669 Canvas::Handle
670 Canvas::create()
671 {
672         return new Canvas("Untitled");
673 }
674
675 void
676 Canvas::push_back(etl::handle<Layer> x)
677 {
678 //      int i(x->count());
679         insert(end(),x);
680         //if(x->count()!=i+1)synfig::info("push_back before %d, after %d",i,x->count());
681 }
682
683 void
684 Canvas::push_front(etl::handle<Layer> x)
685 {
686 //      int i(x->count());
687         insert(begin(),x);
688         //if(x->count()!=i+1)synfig::error("push_front before %d, after %d",i,x->count());
689 }
690
691 void
692 Canvas::insert(iterator iter,etl::handle<Layer> x)
693 {
694 //      int i(x->count());
695         CanvasBase::insert(iter,x);
696
697         /*if(x->count()!=i+1)
698         {
699                 synfig::error(__FILE__":%d: Canvas::insert(): ***FAILURE*** before %d, after %d",__LINE__,i,x->count());
700                 return;
701                 //throw runtime_error("Canvas Insertion Failed");
702         }*/
703
704         x->set_canvas(this);
705
706         add_child(x.get());
707
708         LooseHandle correct_canvas(this);
709         //while(correct_canvas->is_inline())correct_canvas=correct_canvas->parent();
710         Layer::LooseHandle loose_layer(x);
711
712         add_connection(loose_layer,
713                                    sigc::connection::connection(
714                                            x->signal_added_to_group().connect(
715                                                    sigc::bind(
716                                                            sigc::mem_fun(
717                                                                    *correct_canvas,
718                                                                    &Canvas::add_group_pair),
719                                                            loose_layer))));
720         add_connection(loose_layer,
721                                    sigc::connection::connection(
722                                            x->signal_removed_from_group().connect(
723                                                    sigc::bind(
724                                                            sigc::mem_fun(
725                                                                    *correct_canvas,
726                                                                    &Canvas::remove_group_pair),
727                                                            loose_layer))));
728
729         if(!x->get_group().empty())
730                 add_group_pair(x->get_group(),x);
731
732         changed();
733 }
734
735 void
736 Canvas::push_back_simple(etl::handle<Layer> x)
737 {
738         CanvasBase::insert(end(),x);
739         changed();
740 }
741
742 void
743 Canvas::erase(iterator iter)
744 {
745         if(!(*iter)->get_group().empty())
746                 remove_group_pair((*iter)->get_group(),(*iter));
747
748         // HACK: We really shouldn't be wiping
749         // out these signals entirely. We should
750         // only be removing the specific connections
751         // that we made. At the moment, I'm too
752         // lazy to add the code to keep track
753         // of those connections, and no one else
754         // is using these signals, so I'll just
755         // leave these next two lines like they
756         // are for now - darco 07-30-2004
757
758         // so don't wipe them out entirely
759         // - dooglus 09-21-2007
760         disconnect_connections(*iter);
761
762         if(!op_flag_)remove_child(iter->get());
763
764         CanvasBase::erase(iter);
765         if(!op_flag_)changed();
766 }
767
768 Canvas::Handle
769 Canvas::clone(const GUID& deriv_guid)const
770 {
771         synfig::String name;
772         if(is_inline())
773                 name="inline";
774         else
775         {
776                 name=get_id()+"_CLONE";
777
778                 throw runtime_error("Cloning of non-inline canvases is not yet supported");
779         }
780
781         Handle canvas(new Canvas(name));
782
783         if(is_inline())
784         {
785                 canvas->is_inline_=true;
786                 // \todo this was setting parent_=0 - is there a reason for that?
787                 // this was causing bug 1838132, where cloning an inline canvas that contains an imported image fails
788                 // it was failing to ascertain the absolute pathname of the imported image, since it needs the pathname
789                 // of the canvas to get that, which is stored in the parent canvas
790                 canvas->parent_=parent();
791                 canvas->rend_desc() = rend_desc();
792                 //canvas->set_inline(parent());
793         }
794
795         canvas->set_guid(get_guid()^deriv_guid);
796
797         const_iterator iter;
798         for(iter=begin();iter!=end();++iter)
799         {
800                 Layer::Handle layer((*iter)->clone(deriv_guid));
801                 if(layer)
802                 {
803                         assert(layer.count()==1);
804                         int presize(size());
805                         canvas->push_back(layer);
806                         if(!(layer.count()>1))
807                         {
808                                 synfig::error("Canvas::clone(): Cloned layer insertion failure!");
809                                 synfig::error("Canvas::clone(): \tlayer.count()=%d",layer.count());
810                                 synfig::error("Canvas::clone(): \tlayer->get_name()=%s",layer->get_name().c_str());
811                                 synfig::error("Canvas::clone(): \tbefore size()=%d",presize);
812                                 synfig::error("Canvas::clone(): \tafter size()=%d",size());
813                         }
814                         assert(layer.count()>1);
815                 }
816                 else
817                 {
818                         synfig::error("Unable to clone layer");
819                 }
820         }
821
822         canvas->signal_group_pair_removed().clear();
823         canvas->signal_group_pair_added().clear();
824
825         return canvas;
826 }
827
828 void
829 Canvas::set_inline(LooseHandle parent)
830 {
831         if(is_inline_ && parent_)
832         {
833
834         }
835
836         id_="inline";
837         is_inline_=true;
838         parent_=parent;
839
840         // Have the parent inherit all of the group stuff
841
842         std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
843
844         for(iter=group_db_.begin();iter!=group_db_.end();++iter)
845         {
846                 parent->group_db_[iter->first].insert(iter->second.begin(),iter->second.end());
847         }
848
849         rend_desc()=parent->rend_desc();
850 }
851
852 Canvas::Handle
853 Canvas::create_inline(Handle parent)
854 {
855         assert(parent);
856         //if(parent->is_inline())
857         //      return create_inline(parent->parent());
858
859         Handle canvas(new Canvas("inline"));
860         canvas->set_inline(parent);
861         return canvas;
862 }
863
864 Canvas::Handle
865 Canvas::new_child_canvas()
866 {
867         if(is_inline() && parent_)
868                 return parent_->new_child_canvas();
869 //              runtime_error("You cannot create a child Canvas in an inline Canvas");
870
871         // Create a new canvas
872         children().push_back(create());
873         Canvas::Handle canvas(children().back());
874
875         canvas->parent_=this;
876
877         canvas->rend_desc()=rend_desc();
878
879         return canvas;
880 }
881
882 Canvas::Handle
883 Canvas::new_child_canvas(const String &id)
884 {
885         if(is_inline() && parent_)
886                 return parent_->new_child_canvas(id);
887 //              runtime_error("You cannot create a child Canvas in an inline Canvas");
888
889         // Create a new canvas
890         children().push_back(create());
891         Canvas::Handle canvas(children().back());
892
893         canvas->set_id(id);
894         canvas->parent_=this;
895         canvas->rend_desc()=rend_desc();
896
897         return canvas;
898 }
899
900 Canvas::Handle
901 Canvas::add_child_canvas(Canvas::Handle child_canvas, const synfig::String& id)
902 {
903         if(is_inline() && parent_)
904                 return parent_->add_child_canvas(child_canvas,id);
905
906         if(child_canvas->parent() && !child_canvas->is_inline())
907                 throw std::runtime_error("Cannot add child canvas because it belongs to someone else!");
908
909         if(!valid_id(id))
910                 throw runtime_error("Invalid ID");
911
912         try
913         {
914                 find_canvas(id);
915                 throw Exception::IDAlreadyExists(id);
916         }
917         catch(Exception::IDNotFound)
918         {
919                 if(child_canvas->is_inline())
920                         child_canvas->is_inline_=false;
921                 child_canvas->id_=id;
922                 children().push_back(child_canvas);
923                 child_canvas->parent_=this;
924         }
925
926         return child_canvas;
927 }
928
929 void
930 Canvas::remove_child_canvas(Canvas::Handle child_canvas)
931 {
932         if(is_inline() && parent_)
933                 return parent_->remove_child_canvas(child_canvas);
934
935         if(child_canvas->parent_!=this)
936                 throw runtime_error("Given child does not belong to me");
937
938         if(find(children().begin(),children().end(),child_canvas)==children().end())
939                 throw Exception::IDNotFound(child_canvas->get_id());
940
941         children().remove(child_canvas);
942
943         child_canvas->parent_=0;
944 }
945
946 void
947 Canvas::set_file_name(const String &file_name)
948 {
949         if(parent())
950                 parent()->set_file_name(file_name);
951         else
952         {
953                 file_name_=file_name;
954                 signal_file_name_changed_();
955         }
956 }
957
958 sigc::signal<void>&
959 Canvas::signal_file_name_changed()
960 {
961         if(parent())
962                 return signal_file_name_changed();
963         else
964                 return signal_file_name_changed_;
965 }
966
967 String
968 Canvas::get_file_name()const
969 {
970         if(parent())
971                 return parent()->get_file_name();
972         return file_name_;
973 }
974
975 String
976 Canvas::get_file_path()const
977 {
978         if(parent())
979                 return parent()->get_file_path();
980         return dirname(file_name_);
981 }
982
983 String
984 Canvas::get_meta_data(const String& key)const
985 {
986         if(!meta_data_.count(key))
987                 return String();
988         return meta_data_.find(key)->second;
989 }
990
991 void
992 Canvas::set_meta_data(const String& key, const String& data)
993 {
994         if(meta_data_[key]!=data)
995         {
996                 meta_data_[key]=data;
997                 signal_meta_data_changed()(key);
998                 signal_meta_data_changed(key)();
999         }
1000 }
1001
1002 void
1003 Canvas::erase_meta_data(const String& key)
1004 {
1005         if(meta_data_.count(key))
1006         {
1007                 meta_data_.erase(key);
1008                 signal_meta_data_changed()(key);
1009                 signal_meta_data_changed(key)();
1010         }
1011 }
1012
1013 std::list<String>
1014 Canvas::get_meta_data_keys()const
1015 {
1016         std::list<String> ret;
1017
1018         std::map<String,String>::const_iterator iter;
1019
1020         for(iter=meta_data_.begin();!(iter==meta_data_.end());++iter)
1021                 ret.push_back(iter->first);
1022
1023         return ret;
1024 }
1025
1026 /* note - the "Motion Blur" and "Duplicate" layers need the dynamic
1027                   parameters of any PasteCanvas layers they loop over to be
1028                   maintained.  When the variables in the following function
1029                   refer to "motion blur", they mean either of these two
1030                   layers. */
1031 void
1032 synfig::optimize_layers(Time time, Context context, Canvas::Handle op_canvas, bool seen_motion_blur_in_parent)
1033 {
1034         Context iter;
1035
1036         std::vector< std::pair<float,Layer::Handle> > sort_list;
1037         int i, motion_blur_i=0; // motion_blur_i is for resolving which layer comes first in the event of a z_depth tie
1038         float motion_blur_z_depth=0; // the z_depth of the least deep motion blur layer in this context
1039         bool seen_motion_blur_locally = false;
1040         bool motion_blurred; // the final result - is this layer blurred or not?
1041
1042         // If the parent didn't cause us to already be motion blurred,
1043         // check whether there's a motion blur in this context,
1044         // and if so, calculate its z_depth.
1045         if (!seen_motion_blur_in_parent)
1046                 for(iter=context,i=0;*iter;iter++,i++)
1047                 {
1048                         Layer::Handle layer=*iter;
1049
1050                         // If the layer isn't active, don't worry about it
1051                         if(!layer->active())
1052                                 continue;
1053
1054                         // Any layer with an amount of zero is implicitly disabled.
1055                         ValueBase value(layer->get_param("amount"));
1056                         if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1057                                 continue;
1058
1059                         if(layer->get_name()=="MotionBlur" || layer->get_name()=="duplicate")
1060                         {
1061                                 float z_depth(layer->get_z_depth()*1.0001+i);
1062
1063                                 // If we've seen a motion blur before in this context...
1064                                 if (seen_motion_blur_locally)
1065                                 {
1066                                         // ... then we're only interested in this one if it's less deep...
1067                                         if (z_depth < motion_blur_z_depth)
1068                                         {
1069                                                 motion_blur_z_depth = z_depth;
1070                                                 motion_blur_i = i;
1071                                         }
1072                                 }
1073                                 // ... otherwise we're always interested in it.
1074                                 else
1075                                 {
1076                                         motion_blur_z_depth = z_depth;
1077                                         motion_blur_i = i;
1078                                         seen_motion_blur_locally = true;
1079                                 }
1080                         }
1081                 }
1082
1083         // Go ahead and start romping through the canvas to paste
1084         for(iter=context,i=0;*iter;iter++,i++)
1085         {
1086                 Layer::Handle layer=*iter;
1087                 float z_depth(layer->get_z_depth()*1.0001+i);
1088
1089                 // If the layer isn't active, don't worry about it
1090                 if(!layer->active())
1091                         continue;
1092
1093                 // Any layer with an amount of zero is implicitly disabled.
1094                 ValueBase value(layer->get_param("amount"));
1095                 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1096                         continue;
1097
1098                 // note: this used to include "&& paste_canvas->get_time_offset()==0", but then
1099                 //               time-shifted layers weren't being sorted by z-depth (bug #1806852)
1100                 if(layer->get_name()=="PasteCanvas")
1101                 {
1102                         Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1103
1104                         // we need to blur the sub canvas if:
1105                         // our parent is blurred,
1106                         // or the child is lower than a local blur,
1107                         // or the child is at the same z_depth as a local blur, but later in the context
1108
1109 #if 0 // DEBUG
1110                         if (seen_motion_blur_in_parent)                                 synfig::info("seen BLUR in parent\n");
1111                         else if (seen_motion_blur_locally)
1112                                 if (z_depth > motion_blur_z_depth)                      synfig::info("paste is deeper than BLUR\n");
1113                                 else if (z_depth == motion_blur_z_depth) {      synfig::info("paste is same depth as BLUR\n");
1114                                         if (i > motion_blur_i)                                  synfig::info("paste is physically deeper than BLUR\n");
1115                                         else                                                                    synfig::info("paste is less physically deep than BLUR\n");
1116                                 } else                                                                          synfig::info("paste is less deep than BLUR\n");
1117                         else                                                                                    synfig::info("no BLUR at all\n");
1118 #endif  // DEBUG
1119
1120                         motion_blurred = (seen_motion_blur_in_parent ||
1121                                                           (seen_motion_blur_locally &&
1122                                                            (z_depth > motion_blur_z_depth ||
1123                                                                 (z_depth == motion_blur_z_depth && i > motion_blur_i))));
1124
1125                         Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1126                         Canvas::Handle paste_sub_canvas = paste_canvas->get_sub_canvas();
1127                         if(paste_sub_canvas)
1128                                 optimize_layers(time, paste_sub_canvas->get_context(),sub_canvas,motion_blurred);
1129
1130 // \todo: uncommenting the following breaks the rendering of at least examples/backdrop.sifz quite severely
1131 // #define SYNFIG_OPTIMIZE_PASTE_CANVAS
1132 #ifdef SYNFIG_OPTIMIZE_PASTE_CANVAS
1133                         Canvas::iterator sub_iter;
1134
1135                         // Determine if we can just remove the paste canvas altogether
1136                         if (paste_canvas->get_blend_method()    == Color::BLEND_COMPOSITE       &&
1137                                 paste_canvas->get_amount()                      == 1.0f                                         &&
1138                                 paste_canvas->get_zoom()                        == 0                                            &&
1139                                 paste_canvas->get_time_offset()         == 0                                            &&
1140                                 paste_canvas->get_origin()                      == Point(0,0)                           )
1141                                 try {
1142                                         for(sub_iter=sub_canvas->begin();sub_iter!=sub_canvas->end();++sub_iter)
1143                                         {
1144                                                 Layer* layer=sub_iter->get();
1145
1146                                                 // any layers that deform end up breaking things
1147                                                 // so do things the old way if we run into anything like this
1148                                                 if(!dynamic_cast<Layer_NoDeform*>(layer))
1149                                                         throw int();
1150
1151                                                 ValueBase value(layer->get_param("blend_method"));
1152                                                 if(value.get_type()!=ValueBase::TYPE_INTEGER || value.get(int())!=(int)Color::BLEND_COMPOSITE)
1153                                                         throw int();
1154                                         }
1155
1156                                         // It has turned out that we don't need a paste canvas
1157                                         // layer, so just go ahead and add all the layers onto
1158                                         // the current stack and be done with it
1159                                         while(sub_canvas->size())
1160                                         {
1161                                                 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,sub_canvas->front()));
1162                                                 //op_canvas->push_back_simple(sub_canvas->front());
1163                                                 sub_canvas->pop_front();
1164                                         }
1165                                         continue;
1166                                 }
1167                                 catch(int)
1168                                 { }
1169 #endif  // SYNFIG_OPTIMIZE_PASTE_CANVAS
1170
1171                         Layer::Handle new_layer(Layer::create("PasteCanvas"));
1172                         dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(false);
1173                         if (motion_blurred)
1174                         {
1175                                 Layer::DynamicParamList dynamic_param_list(paste_canvas->dynamic_param_list());
1176                                 for(Layer::DynamicParamList::const_iterator iter(dynamic_param_list.begin()); iter != dynamic_param_list.end(); ++iter)
1177                                         new_layer->connect_dynamic_param(iter->first, iter->second);
1178                         }
1179                         Layer::ParamList param_list(paste_canvas->get_param_list());
1180                         //param_list.erase("canvas");
1181                         new_layer->set_param_list(param_list);
1182                         dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_sub_canvas(sub_canvas);
1183                         dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(true);
1184                         layer=new_layer;
1185                 }
1186                 else                                    // not a PasteCanvas - does it use blend method 'Straight'?
1187                 {
1188                         /* when we use the 'straight' blend method, every pixel on the layer affects the layers underneath,
1189                          * not just the non-transparent pixels; the following workarea wraps non-pastecanvas layers in a
1190                          * new pastecanvas to ensure that the straight blend affects the full plane, not just the area
1191                          * within the layer's bounding box
1192                          */
1193
1194                         // \todo: this code probably needs modification to work properly with motionblur and duplicate
1195                         etl::handle<Layer_Composite> composite = etl::handle<Layer_Composite>::cast_dynamic(layer);
1196
1197                         /* some layers (such as circle) don't touch pixels that aren't
1198                          * part of the circle, so they don't get blended correctly when
1199                          * using a straight blend.  so we encapsulate the circle, and the
1200                          * encapsulation layer takes care of the transparent pixels
1201                          * for us.  if we do that for all layers, however, then the
1202                          * distortion layers no longer work, since they have no
1203                          * context to work on.  the Layer::reads_context() method
1204                          * returns true for layers which need to be able to see
1205                          * their context.  we can't encapsulate those.
1206                          */
1207                         if (composite &&
1208                                 Color::is_straight(composite->get_blend_method()) &&
1209                                 !composite->reads_context())
1210                         {
1211                                 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1212                                 sub_canvas->push_back(composite = composite->clone());
1213                                 layer = Layer::create("PasteCanvas");
1214                                 composite->set_description(strprintf("Wrapped clone of '%s'", composite->get_non_empty_description().c_str()));
1215                                 layer->set_description(strprintf("PasteCanvas wrapper for '%s'", composite->get_non_empty_description().c_str()));
1216                                 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1217                                 paste_canvas->set_blend_method(composite->get_blend_method());
1218                                 paste_canvas->set_amount(composite->get_amount());
1219                                 sub_canvas->set_time(time); // region and outline don't calculate their bounding rects until their time is set
1220                                 composite->set_blend_method(Color::BLEND_STRAIGHT); // do this before calling set_sub_canvas(), but after set_time()
1221                                 composite->set_amount(1.0f); // after set_time()
1222                                 paste_canvas->set_sub_canvas(sub_canvas);
1223                         }
1224                 }
1225
1226                 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,layer));
1227                 //op_canvas->push_back_simple(layer);
1228         }
1229
1230         //sort_list.sort();
1231         stable_sort(sort_list.begin(),sort_list.end());
1232         std::vector< std::pair<float,Layer::Handle> >::iterator iter2;
1233         for(iter2=sort_list.begin();iter2!=sort_list.end();++iter2)
1234                 op_canvas->push_back_simple(iter2->second);
1235         op_canvas->op_flag_=true;
1236 }
1237
1238 void
1239 Canvas::get_times_vfunc(Node::time_set &set) const
1240 {
1241         const_iterator  i = begin(),
1242                                 iend = end();
1243
1244         for(; i != iend; ++i)
1245         {
1246                 const Node::time_set &tset = (*i)->get_times();
1247                 set.insert(tset.begin(),tset.end());
1248         }
1249 }
1250
1251 std::set<etl::handle<Layer> >
1252 Canvas::get_layers_in_group(const String&group)
1253 {
1254         if(is_inline() && parent_)
1255                 return parent_->get_layers_in_group(group);
1256
1257         if(group_db_.count(group)==0)
1258                 return std::set<etl::handle<Layer> >();
1259         return group_db_.find(group)->second;
1260 }
1261
1262 std::set<String>
1263 Canvas::get_groups()const
1264 {
1265         if(is_inline() && parent_)
1266                 return parent_->get_groups();
1267
1268         std::set<String> ret;
1269         std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
1270         for(iter=group_db_.begin();iter!=group_db_.end();++iter)
1271                 ret.insert(iter->first);
1272         return ret;
1273 }
1274
1275 int
1276 Canvas::get_group_count()const
1277 {
1278         if(is_inline() && parent_)
1279                 return parent_->get_group_count();
1280
1281         return group_db_.size();
1282 }
1283
1284 void
1285 Canvas::add_group_pair(String group, etl::handle<Layer> layer)
1286 {
1287         group_db_[group].insert(layer);
1288         if(group_db_[group].size()==1)
1289                 signal_group_added()(group);
1290         else
1291                 signal_group_changed()(group);
1292
1293         signal_group_pair_added()(group,layer);
1294
1295         if(is_inline()  && parent_)
1296                 return parent_->add_group_pair(group,layer);
1297 }
1298
1299 void
1300 Canvas::remove_group_pair(String group, etl::handle<Layer> layer)
1301 {
1302         group_db_[group].erase(layer);
1303
1304         signal_group_pair_removed()(group,layer);
1305
1306         if(group_db_[group].empty())
1307         {
1308                 group_db_.erase(group);
1309                 signal_group_removed()(group);
1310         }
1311         else
1312                 signal_group_changed()(group);
1313
1314         if(is_inline() && parent_)
1315                 return parent_->remove_group_pair(group,layer);
1316 }
1317
1318 void
1319 Canvas::add_connection(etl::loose_handle<Layer> layer, sigc::connection connection)
1320 {
1321         connections_[layer].push_back(connection);
1322 }
1323
1324 void
1325 Canvas::disconnect_connections(etl::loose_handle<Layer> layer)
1326 {
1327         std::vector<sigc::connection>::iterator iter;
1328         for(iter=connections_[layer].begin();iter!=connections_[layer].end();++iter)
1329                 iter->disconnect();
1330         connections_[layer].clear();
1331 }
1332
1333 void
1334 Canvas::rename_group(const String&old_name,const String&new_name)
1335 {
1336         if(is_inline() && parent_)
1337                 return parent_->rename_group(old_name,new_name);
1338
1339         {
1340                 std::map<String,std::set<etl::handle<Layer> > >::iterator iter;
1341                 iter=group_db_.find(old_name);
1342                 if(iter!=group_db_.end())
1343                 for(++iter;iter!=group_db_.end() && iter->first.find(old_name)==0;iter=group_db_.find(old_name),++iter)
1344                 {
1345                         String name(iter->first,old_name.size(),String::npos);
1346                         name=new_name+name;
1347                         rename_group(iter->first,name);
1348                 }
1349         }
1350
1351         std::set<etl::handle<Layer> > layers(get_layers_in_group(old_name));
1352         std::set<etl::handle<Layer> >::iterator iter;
1353
1354         for(iter=layers.begin();iter!=layers.end();++iter)
1355         {
1356                 (*iter)->remove_from_group(old_name);
1357                 (*iter)->add_to_group(new_name);
1358         }
1359 }