Always use absolute paths in the Canvas::externals_ map.
[synfig.git] / synfig-core / trunk / src / synfig / canvas.cpp
blob2bbb743366aed661dfc3dde7956999c420edee08
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
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.
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
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
26 #define SYNFIG_NO_ANGLE
28 #ifdef USING_PCH
29 # include "pch.h"
30 #else
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
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 "loadcanvas.h"
43 #include <sigc++/bind.h>
45 #endif
47 using namespace synfig;
48 using namespace etl;
49 using namespace std;
51 namespace synfig { extern Canvas::Handle open_canvas(const String &filename, String &errors, String &warnings); };
53 /* === M A C R O S ========================================================= */
55 #define ALLOW_CLONE_NON_INLINE_CANVASES
57 struct _CanvasCounter
59 static int counter;
60 ~_CanvasCounter()
62 if(counter)
63 synfig::error("%d canvases not yet deleted!",counter);
65 } _canvas_counter;
67 int _CanvasCounter::counter(0);
69 /* === G L O B A L S ======================================================= */
71 /* === P R O C E D U R E S ================================================= */
73 /* === M E T H O D S ======================================================= */
75 Canvas::Canvas(const String &id):
76 id_ (id),
77 version_ (CURRENT_CANVAS_VERSION),
78 cur_time_ (0),
79 is_inline_ (false),
80 is_dirty_ (true),
81 op_flag_ (false)
83 _CanvasCounter::counter++;
84 clear();
87 void
88 Canvas::on_changed()
90 is_dirty_=true;
91 Node::on_changed();
94 Canvas::~Canvas()
96 // we were having a crash where pastecanvas layers were still
97 // refering to a canvas after it had been destroyed; this code
98 // will stop the pastecanvas layers from refering to the canvas
99 // before the canvas is destroyed
101 // the set_sub_canvas(0) ends up deleting the parent-child link,
102 // which deletes the current element from the set we're iterating
103 // through, so we have to make sure we've incremented the iterator
104 // before we mess with the pastecanvas
105 std::set<Node*>::iterator iter = parent_set.begin();
106 while (iter != parent_set.end())
108 Layer_PasteCanvas* paste_canvas = dynamic_cast<Layer_PasteCanvas*>(*iter);
109 iter++;
110 if(paste_canvas)
111 paste_canvas->set_sub_canvas(0);
112 else
113 warning("destroyed canvas has a parent that is not a pastecanvas - please report if repeatable");
116 //if(is_inline() && parent_) assert(0);
117 _CanvasCounter::counter--;
118 clear();
119 begin_delete();
122 Canvas::iterator
123 Canvas::end()
125 return CanvasBase::end()-1;
128 Canvas::const_iterator
129 Canvas::end()const
131 return CanvasBase::end()-1;
134 Canvas::reverse_iterator
135 Canvas::rbegin()
137 return CanvasBase::rbegin()+1;
140 Canvas::const_reverse_iterator
141 Canvas::rbegin()const
143 return CanvasBase::rbegin()+1;
147 Canvas::size()const
149 return CanvasBase::size()-1;
152 void
153 Canvas::clear()
155 while(!empty())
157 Layer::Handle layer(front());
158 //if(layer->count()>2)synfig::info("before layer->count()=%d",layer->count());
160 erase(begin());
161 //if(layer->count()>1)synfig::info("after layer->count()=%d",layer->count());
163 //CanvasBase::clear();
165 // We need to keep a blank handle at the
166 // end of the image list, and acts at
167 // the bottom. Without it, the layers
168 // would just continue going when polled
169 // for a color.
170 CanvasBase::push_back(Layer::Handle());
172 changed();
175 bool
176 Canvas::empty()const
178 return CanvasBase::size()<=1;
181 Layer::Handle &
182 Canvas::back()
184 return *(CanvasBase::end()-1);
187 const Layer::Handle &
188 Canvas::back()const
190 return *(CanvasBase::end()-1);
193 Context
194 Canvas::get_context()const
196 return begin();
199 const ValueNodeList &
200 Canvas::value_node_list()const
202 if(is_inline() && parent_)
203 return parent_->value_node_list();
204 return value_node_list_;
207 KeyframeList &
208 Canvas::keyframe_list()
210 if(is_inline() && parent_)
211 return parent_->keyframe_list();
212 return keyframe_list_;
215 const KeyframeList &
216 Canvas::keyframe_list()const
218 if(is_inline() && parent_)
219 return parent_->keyframe_list();
220 return keyframe_list_;
223 etl::handle<Layer>
224 Canvas::find_layer(const Point &pos)
226 return get_context().hit_check(pos);
229 static bool
230 valid_id(const String &x)
232 static const char bad_chars[]=" :#@$^&()*";
233 unsigned int i;
235 if(!x.empty() && x[0]>='0' && x[0]<='9')
236 return false;
238 for(i=0;i<sizeof(bad_chars);i++)
239 if(x.find_first_of(bad_chars[i])!=string::npos)
240 return false;
242 return true;
245 void
246 Canvas::set_id(const String &x)
248 if(is_inline() && parent_)
249 throw runtime_error("Inline Canvas cannot have an ID");
251 if(!valid_id(x))
252 throw runtime_error("Invalid ID");
253 id_=x;
254 signal_id_changed_();
257 void
258 Canvas::set_name(const String &x)
260 name_=x;
261 signal_meta_data_changed()("name");
262 signal_meta_data_changed("name")();
265 void
266 Canvas::set_author(const String &x)
268 author_=x;
269 signal_meta_data_changed()("author");
270 signal_meta_data_changed("author")();
273 void
274 Canvas::set_description(const String &x)
276 description_=x;
277 signal_meta_data_changed()("description");
278 signal_meta_data_changed("description")();
281 void
282 Canvas::set_time(Time t)const
284 if(is_dirty_ || !get_time().is_equal(t))
286 #if 0
287 if(is_root())
289 synfig::info("is_dirty_=%d",is_dirty_);
290 synfig::info("get_time()=%f",(float)get_time());
291 synfig::info("t=%f",(float)t);
293 #endif
295 // ...questionable
296 const_cast<Canvas&>(*this).cur_time_=t;
298 is_dirty_=false;
299 get_context().set_time(t);
301 is_dirty_=false;
304 Canvas::LooseHandle
305 Canvas::get_root()const
307 return parent_?parent_->get_root().get():const_cast<synfig::Canvas *>(this);
311 Canvas::get_depth(etl::handle<Layer> layer)const
313 const_iterator iter;
314 int i(0);
315 for(iter=begin();iter!=end();++iter,i++)
317 if(layer==*iter)
318 return i;
320 return -1;
323 String
324 Canvas::get_relative_id(etl::loose_handle<const Canvas> x)const
326 if(x->get_root()==this)
327 return ":";
328 if(is_inline() && parent_)
329 return parent_->_get_relative_id(x);
330 return _get_relative_id(x);
333 String
334 Canvas::_get_relative_id(etl::loose_handle<const Canvas> x)const
336 if(is_inline() && parent_)
337 return parent_->_get_relative_id(x);
339 if(x.get()==this)
340 return String();
342 if(parent()==x.get())
343 return get_id();
345 String id;
347 const Canvas* canvas=this;
349 for(;!canvas->is_root();canvas=canvas->parent().get())
350 id=':'+canvas->get_id()+id;
352 if(x && get_root()!=x->get_root())
354 //String file_name=get_file_name();
355 //String file_path=x->get_file_path();
357 String file_name;
358 if(is_absolute_path(get_file_name()))
359 file_name=etl::relative_path(x->get_file_path(),get_file_name());
360 else
361 file_name=get_file_name();
363 // If the path of X is inside of file_name,
364 // then remove it.
365 //if(file_name.size()>file_path.size())
366 // if(file_path==String(file_name,0,file_path.size()))
367 // file_name.erase(0,file_path.size()+1);
369 id=file_name+'#'+id;
372 return id;
375 ValueNode::Handle
376 Canvas::find_value_node(const String &id)
378 return
379 ValueNode::Handle::cast_const(
380 const_cast<const Canvas*>(this)->find_value_node(id)
384 ValueNode::ConstHandle
385 Canvas::find_value_node(const String &id)const
387 if(is_inline() && parent_)
388 return parent_->find_value_node(id);
390 if(id.empty())
391 throw Exception::IDNotFound("Empty ID");
393 // If we do not have any resolution, then we assume that the
394 // request is for this immediate canvas
395 if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
396 return value_node_list_.find(id);
398 String canvas_id(id,0,id.rfind(':'));
399 String value_node_id(id,id.rfind(':')+1);
400 if(canvas_id.empty())
401 canvas_id=':';
402 //synfig::warning("constfind:value_node_id: "+value_node_id);
403 //synfig::warning("constfind:canvas_id: "+canvas_id);
405 String warnings;
406 return find_canvas(canvas_id, warnings)->value_node_list_.find(value_node_id);
409 ValueNode::Handle
410 Canvas::surefind_value_node(const String &id)
412 if(is_inline() && parent_)
413 return parent_->surefind_value_node(id);
415 if(id.empty())
416 throw Exception::IDNotFound("Empty ID");
418 // If we do not have any resolution, then we assume that the
419 // request is for this immediate canvas
420 if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
421 return value_node_list_.surefind(id);
423 String canvas_id(id,0,id.rfind(':'));
424 String value_node_id(id,id.rfind(':')+1);
425 if(canvas_id.empty())
426 canvas_id=':';
428 String warnings;
429 return surefind_canvas(canvas_id,warnings)->value_node_list_.surefind(value_node_id);
432 void
433 Canvas::add_value_node(ValueNode::Handle x, const String &id)
435 if(is_inline() && parent_)
436 return parent_->add_value_node(x,id);
437 // throw runtime_error("You cannot add a ValueNode to an inline Canvas");
439 if(x->is_exported())
440 throw runtime_error("ValueNode is already exported");
442 if(id.empty())
443 throw Exception::BadLinkName("Empty ID");
445 if(id.find_first_of(':',0)!=string::npos)
446 throw Exception::BadLinkName("Bad character");
450 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
451 throw Exception::IDNotFound("add_value_node()");
453 throw Exception::IDAlreadyExists(id);
455 catch(Exception::IDNotFound)
457 x->set_id(id);
459 x->set_parent_canvas(this);
461 if(!value_node_list_.add(x))
463 synfig::error("Unable to add ValueNode");
464 throw std::runtime_error("Unable to add ValueNode");
467 return;
472 void
473 Canvas::rename_value_node(ValueNode::Handle x, const String &id)
475 if(id.empty())
476 throw Exception::BadLinkName("Empty ID");
478 if(id.find_first_of(": ",0)!=string::npos)
479 throw Exception::BadLinkName("Bad character");
483 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
484 throw Exception::IDNotFound("rename_value_node");
485 throw Exception::IDAlreadyExists(id);
487 catch(Exception::IDNotFound)
489 x->set_id(id);
491 return;
496 void
497 Canvas::remove_value_node(ValueNode::Handle x)
499 if(is_inline() && parent_)
500 return parent_->remove_value_node(x);
501 // throw Exception::IDNotFound("Canvas::remove_value_node() was called from an inline canvas");
503 if(!x)
504 throw Exception::IDNotFound("Canvas::remove_value_node() was passed empty handle");
506 if(!value_node_list_.erase(x))
507 throw Exception::IDNotFound("Canvas::remove_value_node(): ValueNode was not found inside of this canvas");
509 //x->set_parent_canvas(0);
511 x->set_id("");
514 Canvas::Handle
515 Canvas::surefind_canvas(const String &id, String &warnings)
517 if(is_inline() && parent_)
518 return parent_->surefind_canvas(id,warnings);
520 if(id.empty())
521 return this;
523 // If the ID contains a "#" character, then a filename is
524 // expected on the left side.
525 if(id.find_first_of('#')!=string::npos)
527 // If '#' is the first character, remove it
528 // and attempt to parse the ID again.
529 if(id[0]=='#')
530 return surefind_canvas(String(id,1),warnings);
532 //! \todo This needs a lot more optimization
533 String file_name(id,0,id.find_first_of('#'));
534 String external_id(id,id.find_first_of('#')+1);
536 file_name=unix_to_local_path(file_name);
538 Canvas::Handle external_canvas;
540 if(!is_absolute_path(file_name))
541 file_name = get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name;
543 // If the composition is already open, then use it.
544 if(externals_.count(file_name))
545 external_canvas=externals_[file_name];
546 else
548 String errors;
549 external_canvas=open_canvas(file_name, errors, warnings);
550 if(!external_canvas)
551 throw runtime_error(errors);
552 externals_[file_name]=external_canvas;
555 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id, warnings));
558 // If we do not have any resolution, then we assume that the
559 // request is for this immediate canvas
560 if(id.find_first_of(':')==string::npos)
562 Children::iterator iter;
564 // Search for the image in the image list,
565 // and return it if it is found
566 for(iter=children().begin();iter!=children().end();iter++)
567 if(id==(*iter)->get_id())
568 return *iter;
570 // Create a new canvas and return it
571 //synfig::warning("Implicitly creating canvas named "+id);
572 return new_child_canvas(id);
575 // If the first character is the separator, then
576 // this references the root canvas.
577 if(id[0]==':')
578 return get_root()->surefind_canvas(string(id,1),warnings);
580 // Now we know that the requested Canvas is in a child
581 // of this canvas. We have to find that canvas and
582 // call "find_canvas" on it, and return the result.
584 String canvas_name=string(id,0,id.find_first_of(':'));
586 Canvas::Handle child_canvas=surefind_canvas(canvas_name,warnings);
588 return child_canvas->surefind_canvas(string(id,id.find_first_of(':')+1),warnings);
591 Canvas::Handle
592 Canvas::find_canvas(const String &id, String &warnings)
594 return
595 Canvas::Handle::cast_const(
596 const_cast<const Canvas*>(this)->find_canvas(id, warnings)
600 Canvas::ConstHandle
601 Canvas::find_canvas(const String &id, String &warnings)const
603 if(is_inline() && parent_)
604 return parent_->find_canvas(id, warnings);
606 if(id.empty())
607 return this;
609 // If the ID contains a "#" character, then a filename is
610 // expected on the left side.
611 if(id.find_first_of('#')!=string::npos)
613 // If '#' is the first character, remove it
614 // and attempt to parse the ID again.
615 if(id[0]=='#')
616 return find_canvas(String(id,1), warnings);
618 //! \todo This needs a lot more optimization
619 String file_name(id,0,id.find_first_of('#'));
620 String external_id(id,id.find_first_of('#')+1);
622 file_name=unix_to_local_path(file_name);
624 Canvas::Handle external_canvas;
626 if(!is_absolute_path(file_name))
627 file_name = get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name;
629 // If the composition is already open, then use it.
630 if(externals_.count(file_name))
631 external_canvas=externals_[file_name];
632 else
634 String errors, warnings;
635 external_canvas=open_canvas(file_name, errors, warnings);
636 if(!external_canvas)
637 throw runtime_error(errors);
638 externals_[file_name]=external_canvas;
641 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id, warnings));
644 // If we do not have any resolution, then we assume that the
645 // request is for this immediate canvas
646 if(id.find_first_of(':')==string::npos)
648 Children::const_iterator iter;
650 // Search for the image in the image list,
651 // and return it if it is found
652 for(iter=children().begin();iter!=children().end();iter++)
653 if(id==(*iter)->get_id())
654 return *iter;
656 throw Exception::IDNotFound("Child Canvas in Parent Canvas: (child)"+id);
659 // If the first character is the separator, then
660 // this references the root canvas.
661 if(id[0]==':')
662 return get_root()->find_canvas(string(id,1), warnings);
664 // Now we know that the requested Canvas is in a child
665 // of this canvas. We have to find that canvas and
666 // call "find_canvas" on it, and return the result.
668 String canvas_name=string(id,0,id.find_first_of(':'));
670 Canvas::ConstHandle child_canvas=find_canvas(canvas_name, warnings);
672 return child_canvas->find_canvas(string(id,id.find_first_of(':')+1), warnings);
675 Canvas::Handle
676 Canvas::create()
678 return new Canvas("Untitled");
681 void
682 Canvas::push_back(etl::handle<Layer> x)
684 // int i(x->count());
685 insert(end(),x);
686 //if(x->count()!=i+1)synfig::info("push_back before %d, after %d",i,x->count());
689 void
690 Canvas::push_front(etl::handle<Layer> x)
692 // int i(x->count());
693 insert(begin(),x);
694 //if(x->count()!=i+1)synfig::error("push_front before %d, after %d",i,x->count());
697 void
698 Canvas::insert(iterator iter,etl::handle<Layer> x)
700 // int i(x->count());
701 CanvasBase::insert(iter,x);
703 /*if(x->count()!=i+1)
705 synfig::error(__FILE__":%d: Canvas::insert(): ***FAILURE*** before %d, after %d",__LINE__,i,x->count());
706 return;
707 //throw runtime_error("Canvas Insertion Failed");
710 x->set_canvas(this);
712 add_child(x.get());
714 LooseHandle correct_canvas(this);
715 //while(correct_canvas->is_inline())correct_canvas=correct_canvas->parent();
716 Layer::LooseHandle loose_layer(x);
718 add_connection(loose_layer,
719 sigc::connection::connection(
720 x->signal_added_to_group().connect(
721 sigc::bind(
722 sigc::mem_fun(
723 *correct_canvas,
724 &Canvas::add_group_pair),
725 loose_layer))));
726 add_connection(loose_layer,
727 sigc::connection::connection(
728 x->signal_removed_from_group().connect(
729 sigc::bind(
730 sigc::mem_fun(
731 *correct_canvas,
732 &Canvas::remove_group_pair),
733 loose_layer))));
735 if(!x->get_group().empty())
736 add_group_pair(x->get_group(),x);
738 changed();
741 void
742 Canvas::push_back_simple(etl::handle<Layer> x)
744 CanvasBase::insert(end(),x);
745 changed();
748 void
749 Canvas::erase(iterator iter)
751 if(!(*iter)->get_group().empty())
752 remove_group_pair((*iter)->get_group(),(*iter));
754 // HACK: We really shouldn't be wiping
755 // out these signals entirely. We should
756 // only be removing the specific connections
757 // that we made. At the moment, I'm too
758 // lazy to add the code to keep track
759 // of those connections, and no one else
760 // is using these signals, so I'll just
761 // leave these next two lines like they
762 // are for now - darco 07-30-2004
764 // so don't wipe them out entirely
765 // - dooglus 09-21-2007
766 disconnect_connections(*iter);
768 if(!op_flag_)remove_child(iter->get());
770 CanvasBase::erase(iter);
771 if(!op_flag_)changed();
774 Canvas::Handle
775 Canvas::clone(const GUID& deriv_guid)const
777 synfig::String name;
778 if(is_inline())
779 name="inline";
780 else
782 name=get_id()+"_CLONE";
784 #ifndef ALLOW_CLONE_NON_INLINE_CANVASES
785 throw runtime_error("Cloning of non-inline canvases is not yet supported");
786 #endif // ALLOW_CLONE_NON_INLINE_CANVASES
789 Handle canvas(new Canvas(name));
791 if(is_inline())
793 canvas->is_inline_=true;
794 // \todo this was setting parent_=0 - is there a reason for that?
795 // this was causing bug 1838132, where cloning an inline canvas that contains an imported image fails
796 // it was failing to ascertain the absolute pathname of the imported image, since it needs the pathname
797 // of the canvas to get that, which is stored in the parent canvas
798 canvas->parent_=parent();
799 canvas->rend_desc() = rend_desc();
800 //canvas->set_inline(parent());
803 canvas->set_guid(get_guid()^deriv_guid);
805 const_iterator iter;
806 for(iter=begin();iter!=end();++iter)
808 Layer::Handle layer((*iter)->clone(deriv_guid));
809 if(layer)
811 assert(layer.count()==1);
812 int presize(size());
813 canvas->push_back(layer);
814 if(!(layer.count()>1))
816 synfig::error("Canvas::clone(): Cloned layer insertion failure!");
817 synfig::error("Canvas::clone(): \tlayer.count()=%d",layer.count());
818 synfig::error("Canvas::clone(): \tlayer->get_name()=%s",layer->get_name().c_str());
819 synfig::error("Canvas::clone(): \tbefore size()=%d",presize);
820 synfig::error("Canvas::clone(): \tafter size()=%d",size());
822 assert(layer.count()>1);
824 else
826 synfig::error("Unable to clone layer");
830 canvas->signal_group_pair_removed().clear();
831 canvas->signal_group_pair_added().clear();
833 return canvas;
836 void
837 Canvas::set_inline(LooseHandle parent)
839 if(is_inline_ && parent_)
844 id_="inline";
845 is_inline_=true;
846 parent_=parent;
848 // Have the parent inherit all of the group stuff
850 std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
852 for(iter=group_db_.begin();iter!=group_db_.end();++iter)
854 parent->group_db_[iter->first].insert(iter->second.begin(),iter->second.end());
857 rend_desc()=parent->rend_desc();
860 Canvas::Handle
861 Canvas::create_inline(Handle parent)
863 assert(parent);
864 //if(parent->is_inline())
865 // return create_inline(parent->parent());
867 Handle canvas(new Canvas("inline"));
868 canvas->set_inline(parent);
869 return canvas;
872 Canvas::Handle
873 Canvas::new_child_canvas()
875 if(is_inline() && parent_)
876 return parent_->new_child_canvas();
877 // runtime_error("You cannot create a child Canvas in an inline Canvas");
879 // Create a new canvas
880 children().push_back(create());
881 Canvas::Handle canvas(children().back());
883 canvas->parent_=this;
885 canvas->rend_desc()=rend_desc();
887 return canvas;
890 Canvas::Handle
891 Canvas::new_child_canvas(const String &id)
893 if(is_inline() && parent_)
894 return parent_->new_child_canvas(id);
895 // runtime_error("You cannot create a child Canvas in an inline Canvas");
897 // Create a new canvas
898 children().push_back(create());
899 Canvas::Handle canvas(children().back());
901 canvas->set_id(id);
902 canvas->parent_=this;
903 canvas->rend_desc()=rend_desc();
905 return canvas;
908 Canvas::Handle
909 Canvas::add_child_canvas(Canvas::Handle child_canvas, const synfig::String& id)
911 if(is_inline() && parent_)
912 return parent_->add_child_canvas(child_canvas,id);
914 if(child_canvas->parent() && !child_canvas->is_inline())
915 throw std::runtime_error("Cannot add child canvas because it belongs to someone else!");
917 if(!valid_id(id))
918 throw runtime_error("Invalid ID");
922 String warnings;
923 find_canvas(id, warnings);
924 throw Exception::IDAlreadyExists(id);
926 catch(Exception::IDNotFound)
928 if(child_canvas->is_inline())
929 child_canvas->is_inline_=false;
930 child_canvas->id_=id;
931 children().push_back(child_canvas);
932 child_canvas->parent_=this;
935 return child_canvas;
938 void
939 Canvas::remove_child_canvas(Canvas::Handle child_canvas)
941 if(is_inline() && parent_)
942 return parent_->remove_child_canvas(child_canvas);
944 if(child_canvas->parent_!=this)
945 throw runtime_error("Given child does not belong to me");
947 if(find(children().begin(),children().end(),child_canvas)==children().end())
948 throw Exception::IDNotFound(child_canvas->get_id());
950 children().remove(child_canvas);
952 child_canvas->parent_=0;
955 void
956 Canvas::set_file_name(const String &file_name)
958 if(parent())
959 parent()->set_file_name(file_name);
960 else
962 String old_name(file_name_);
963 file_name_=file_name;
965 // when a canvas is made, its name is ""
966 // then, before it's saved or even edited, it gets a name like "Synfig Animation 23", in the local language
967 // we don't want to register the canvas' filename in the canvas map until it gets a real filename
968 if (old_name != "")
970 file_name_=file_name;
971 std::map<synfig::String, etl::loose_handle<Canvas> >::iterator iter;
972 for(iter=get_open_canvas_map().begin();iter!=get_open_canvas_map().end();++iter)
973 if(iter->second==this)
974 break;
975 if (iter == get_open_canvas_map().end())
976 CanvasParser::register_canvas_in_map(this, file_name);
977 else
978 signal_file_name_changed_();
983 sigc::signal<void>&
984 Canvas::signal_file_name_changed()
986 if(parent())
987 return parent()->signal_file_name_changed();
988 else
989 return signal_file_name_changed_;
992 String
993 Canvas::get_file_name()const
995 if(parent())
996 return parent()->get_file_name();
997 return file_name_;
1000 String
1001 Canvas::get_file_path()const
1003 if(parent())
1004 return parent()->get_file_path();
1005 return dirname(file_name_);
1008 String
1009 Canvas::get_meta_data(const String& key)const
1011 if(!meta_data_.count(key))
1012 return String();
1013 return meta_data_.find(key)->second;
1016 void
1017 Canvas::set_meta_data(const String& key, const String& data)
1019 if(meta_data_[key]!=data)
1021 meta_data_[key]=data;
1022 signal_meta_data_changed()(key);
1023 signal_meta_data_changed(key)();
1027 void
1028 Canvas::erase_meta_data(const String& key)
1030 if(meta_data_.count(key))
1032 meta_data_.erase(key);
1033 signal_meta_data_changed()(key);
1034 signal_meta_data_changed(key)();
1038 std::list<String>
1039 Canvas::get_meta_data_keys()const
1041 std::list<String> ret;
1043 std::map<String,String>::const_iterator iter;
1045 for(iter=meta_data_.begin();!(iter==meta_data_.end());++iter)
1046 ret.push_back(iter->first);
1048 return ret;
1051 /* note - the "Motion Blur" and "Duplicate" layers need the dynamic
1052 parameters of any PasteCanvas layers they loop over to be
1053 maintained. When the variables in the following function
1054 refer to "motion blur", they mean either of these two
1055 layers. */
1056 void
1057 synfig::optimize_layers(Time time, Context context, Canvas::Handle op_canvas, bool seen_motion_blur_in_parent)
1059 Context iter;
1061 std::vector< std::pair<float,Layer::Handle> > sort_list;
1062 int i, motion_blur_i=0; // motion_blur_i is for resolving which layer comes first in the event of a z_depth tie
1063 float motion_blur_z_depth=0; // the z_depth of the least deep motion blur layer in this context
1064 bool seen_motion_blur_locally = false;
1065 bool motion_blurred; // the final result - is this layer blurred or not?
1067 // If the parent didn't cause us to already be motion blurred,
1068 // check whether there's a motion blur in this context,
1069 // and if so, calculate its z_depth.
1070 if (!seen_motion_blur_in_parent)
1071 for(iter=context,i=0;*iter;iter++,i++)
1073 Layer::Handle layer=*iter;
1075 // If the layer isn't active, don't worry about it
1076 if(!layer->active())
1077 continue;
1079 // Any layer with an amount of zero is implicitly disabled.
1080 ValueBase value(layer->get_param("amount"));
1081 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1082 continue;
1084 if(layer->get_name()=="MotionBlur" || layer->get_name()=="duplicate")
1086 float z_depth(layer->get_z_depth()*1.0001+i);
1088 // If we've seen a motion blur before in this context...
1089 if (seen_motion_blur_locally)
1091 // ... then we're only interested in this one if it's less deep...
1092 if (z_depth < motion_blur_z_depth)
1094 motion_blur_z_depth = z_depth;
1095 motion_blur_i = i;
1098 // ... otherwise we're always interested in it.
1099 else
1101 motion_blur_z_depth = z_depth;
1102 motion_blur_i = i;
1103 seen_motion_blur_locally = true;
1108 // Go ahead and start romping through the canvas to paste
1109 for(iter=context,i=0;*iter;iter++,i++)
1111 Layer::Handle layer=*iter;
1112 float z_depth(layer->get_z_depth()*1.0001+i);
1114 // If the layer isn't active, don't worry about it
1115 if(!layer->active())
1116 continue;
1118 // Any layer with an amount of zero is implicitly disabled.
1119 ValueBase value(layer->get_param("amount"));
1120 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1121 continue;
1123 // note: this used to include "&& paste_canvas->get_time_offset()==0", but then
1124 // time-shifted layers weren't being sorted by z-depth (bug #1806852)
1125 if(layer->get_name()=="PasteCanvas")
1127 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1129 // we need to blur the sub canvas if:
1130 // our parent is blurred,
1131 // or the child is lower than a local blur,
1132 // or the child is at the same z_depth as a local blur, but later in the context
1134 #if 0 // DEBUG
1135 if (seen_motion_blur_in_parent) synfig::info("seen BLUR in parent\n");
1136 else if (seen_motion_blur_locally)
1137 if (z_depth > motion_blur_z_depth) synfig::info("paste is deeper than BLUR\n");
1138 else if (z_depth == motion_blur_z_depth) { synfig::info("paste is same depth as BLUR\n");
1139 if (i > motion_blur_i) synfig::info("paste is physically deeper than BLUR\n");
1140 else synfig::info("paste is less physically deep than BLUR\n");
1141 } else synfig::info("paste is less deep than BLUR\n");
1142 else synfig::info("no BLUR at all\n");
1143 #endif // DEBUG
1145 motion_blurred = (seen_motion_blur_in_parent ||
1146 (seen_motion_blur_locally &&
1147 (z_depth > motion_blur_z_depth ||
1148 (z_depth == motion_blur_z_depth && i > motion_blur_i))));
1150 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1151 Canvas::Handle paste_sub_canvas = paste_canvas->get_sub_canvas();
1152 if(paste_sub_canvas)
1153 optimize_layers(time, paste_sub_canvas->get_context(),sub_canvas,motion_blurred);
1155 // \todo: uncommenting the following breaks the rendering of at least examples/backdrop.sifz quite severely
1156 // #define SYNFIG_OPTIMIZE_PASTE_CANVAS
1157 #ifdef SYNFIG_OPTIMIZE_PASTE_CANVAS
1158 Canvas::iterator sub_iter;
1160 // Determine if we can just remove the paste canvas altogether
1161 if (paste_canvas->get_blend_method() == Color::BLEND_COMPOSITE &&
1162 paste_canvas->get_amount() == 1.0f &&
1163 paste_canvas->get_zoom() == 0 &&
1164 paste_canvas->get_time_offset() == 0 &&
1165 paste_canvas->get_origin() == Point(0,0) )
1166 try {
1167 for(sub_iter=sub_canvas->begin();sub_iter!=sub_canvas->end();++sub_iter)
1169 Layer* layer=sub_iter->get();
1171 // any layers that deform end up breaking things
1172 // so do things the old way if we run into anything like this
1173 if(!dynamic_cast<Layer_NoDeform*>(layer))
1174 throw int();
1176 ValueBase value(layer->get_param("blend_method"));
1177 if(value.get_type()!=ValueBase::TYPE_INTEGER || value.get(int())!=(int)Color::BLEND_COMPOSITE)
1178 throw int();
1181 // It has turned out that we don't need a paste canvas
1182 // layer, so just go ahead and add all the layers onto
1183 // the current stack and be done with it
1184 while(sub_canvas->size())
1186 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,sub_canvas->front()));
1187 //op_canvas->push_back_simple(sub_canvas->front());
1188 sub_canvas->pop_front();
1190 continue;
1192 catch(int)
1194 #endif // SYNFIG_OPTIMIZE_PASTE_CANVAS
1196 Layer::Handle new_layer(Layer::create("PasteCanvas"));
1197 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(false);
1198 if (motion_blurred)
1200 Layer::DynamicParamList dynamic_param_list(paste_canvas->dynamic_param_list());
1201 for(Layer::DynamicParamList::const_iterator iter(dynamic_param_list.begin()); iter != dynamic_param_list.end(); ++iter)
1202 new_layer->connect_dynamic_param(iter->first, iter->second);
1204 Layer::ParamList param_list(paste_canvas->get_param_list());
1205 //param_list.erase("canvas");
1206 new_layer->set_param_list(param_list);
1207 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_sub_canvas(sub_canvas);
1208 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(true);
1209 layer=new_layer;
1211 else // not a PasteCanvas - does it use blend method 'Straight'?
1213 /* when we use the 'straight' blend method, every pixel on the layer affects the layers underneath,
1214 * not just the non-transparent pixels; the following workarea wraps non-pastecanvas layers in a
1215 * new pastecanvas to ensure that the straight blend affects the full plane, not just the area
1216 * within the layer's bounding box
1219 // \todo: this code probably needs modification to work properly with motionblur and duplicate
1220 etl::handle<Layer_Composite> composite = etl::handle<Layer_Composite>::cast_dynamic(layer);
1222 /* some layers (such as circle) don't touch pixels that aren't
1223 * part of the circle, so they don't get blended correctly when
1224 * using a straight blend. so we encapsulate the circle, and the
1225 * encapsulation layer takes care of the transparent pixels
1226 * for us. if we do that for all layers, however, then the
1227 * distortion layers no longer work, since they have no
1228 * context to work on. the Layer::reads_context() method
1229 * returns true for layers which need to be able to see
1230 * their context. we can't encapsulate those.
1232 if (composite &&
1233 Color::is_straight(composite->get_blend_method()) &&
1234 !composite->reads_context())
1236 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1237 // don't use clone() because it re-randomizes the seeds of any random valuenodes
1238 sub_canvas->push_back(composite = composite->simple_clone());
1239 layer = Layer::create("PasteCanvas");
1240 composite->set_description(strprintf("Wrapped clone of '%s'", composite->get_non_empty_description().c_str()));
1241 layer->set_description(strprintf("PasteCanvas wrapper for '%s'", composite->get_non_empty_description().c_str()));
1242 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1243 paste_canvas->set_blend_method(composite->get_blend_method());
1244 paste_canvas->set_amount(composite->get_amount());
1245 sub_canvas->set_time(time); // region and outline don't calculate their bounding rects until their time is set
1246 composite->set_blend_method(Color::BLEND_STRAIGHT); // do this before calling set_sub_canvas(), but after set_time()
1247 composite->set_amount(1.0f); // after set_time()
1248 paste_canvas->set_sub_canvas(sub_canvas);
1252 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,layer));
1253 //op_canvas->push_back_simple(layer);
1256 //sort_list.sort();
1257 stable_sort(sort_list.begin(),sort_list.end());
1258 std::vector< std::pair<float,Layer::Handle> >::iterator iter2;
1259 for(iter2=sort_list.begin();iter2!=sort_list.end();++iter2)
1260 op_canvas->push_back_simple(iter2->second);
1261 op_canvas->op_flag_=true;
1264 void
1265 Canvas::get_times_vfunc(Node::time_set &set) const
1267 const_iterator i = begin(),
1268 iend = end();
1270 for(; i != iend; ++i)
1272 const Node::time_set &tset = (*i)->get_times();
1273 set.insert(tset.begin(),tset.end());
1277 std::set<etl::handle<Layer> >
1278 Canvas::get_layers_in_group(const String&group)
1280 if(is_inline() && parent_)
1281 return parent_->get_layers_in_group(group);
1283 if(group_db_.count(group)==0)
1284 return std::set<etl::handle<Layer> >();
1285 return group_db_.find(group)->second;
1288 std::set<String>
1289 Canvas::get_groups()const
1291 if(is_inline() && parent_)
1292 return parent_->get_groups();
1294 std::set<String> ret;
1295 std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
1296 for(iter=group_db_.begin();iter!=group_db_.end();++iter)
1297 ret.insert(iter->first);
1298 return ret;
1302 Canvas::get_group_count()const
1304 if(is_inline() && parent_)
1305 return parent_->get_group_count();
1307 return group_db_.size();
1310 void
1311 Canvas::add_group_pair(String group, etl::handle<Layer> layer)
1313 group_db_[group].insert(layer);
1314 if(group_db_[group].size()==1)
1315 signal_group_added()(group);
1316 else
1317 signal_group_changed()(group);
1319 signal_group_pair_added()(group,layer);
1321 if(is_inline() && parent_)
1322 return parent_->add_group_pair(group,layer);
1325 void
1326 Canvas::remove_group_pair(String group, etl::handle<Layer> layer)
1328 group_db_[group].erase(layer);
1330 signal_group_pair_removed()(group,layer);
1332 if(group_db_[group].empty())
1334 group_db_.erase(group);
1335 signal_group_removed()(group);
1337 else
1338 signal_group_changed()(group);
1340 if(is_inline() && parent_)
1341 return parent_->remove_group_pair(group,layer);
1344 void
1345 Canvas::add_connection(etl::loose_handle<Layer> layer, sigc::connection connection)
1347 connections_[layer].push_back(connection);
1350 void
1351 Canvas::disconnect_connections(etl::loose_handle<Layer> layer)
1353 std::vector<sigc::connection>::iterator iter;
1354 for(iter=connections_[layer].begin();iter!=connections_[layer].end();++iter)
1355 iter->disconnect();
1356 connections_[layer].clear();
1359 void
1360 Canvas::rename_group(const String&old_name,const String&new_name)
1362 if(is_inline() && parent_)
1363 return parent_->rename_group(old_name,new_name);
1366 std::map<String,std::set<etl::handle<Layer> > >::iterator iter;
1367 iter=group_db_.find(old_name);
1368 if(iter!=group_db_.end())
1369 for(++iter;iter!=group_db_.end() && iter->first.find(old_name)==0;iter=group_db_.find(old_name),++iter)
1371 String name(iter->first,old_name.size(),String::npos);
1372 name=new_name+name;
1373 rename_group(iter->first,name);
1377 std::set<etl::handle<Layer> > layers(get_layers_in_group(old_name));
1378 std::set<etl::handle<Layer> >::iterator iter;
1380 for(iter=layers.begin();iter!=layers.end();++iter)
1382 (*iter)->remove_from_group(old_name);
1383 (*iter)->add_to_group(new_name);
1387 void
1388 Canvas::register_external_canvas(String file_name, Handle canvas)
1390 if(!is_absolute_path(file_name)) file_name = get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name;
1391 externals_[file_name] = canvas;
1394 #ifdef _DEBUG
1395 void
1396 Canvas::show_externals(String file, int line, String text) const
1398 printf(" .----- (externals for %lx '%s')\n | %s:%d %s\n", ulong(this), get_name().c_str(), file.c_str(), line, text.c_str());
1399 std::map<String, Handle>::iterator iter;
1400 for (iter = externals_.begin(); iter != externals_.end(); iter++)
1402 synfig::String first(iter->first);
1403 etl::loose_handle<Canvas> second(iter->second);
1404 printf(" | %40s : %lx (%d)\n", first.c_str(), ulong(&*second), second->count());
1406 printf(" `-----\n\n");
1408 #endif // _DEBUG