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