When loading .sif files, show any warnings in the GUI. When loading .sif files with...
[synfig.git] / synfig-core / trunk / src / synfig / canvas.cpp
blob1bc1a18afe34225a4a2f9b15232684521ad65677
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 <sigc++/bind.h>
44 #endif
46 using namespace synfig;
47 using namespace etl;
48 using namespace std;
50 namespace synfig { extern Canvas::Handle open_canvas(const String &filename, String &errors, String &warnings); };
52 /* === M A C R O S ========================================================= */
54 struct _CanvasCounter
56 static int counter;
57 ~_CanvasCounter()
59 if(counter)
60 synfig::error("%d canvases not yet deleted!",counter);
62 } _canvas_counter;
64 int _CanvasCounter::counter(0);
66 /* === G L O B A L S ======================================================= */
68 /* === P R O C E D U R E S ================================================= */
70 /* === M E T H O D S ======================================================= */
72 Canvas::Canvas(const String &id):
73 id_ (id),
74 version_ (CURRENT_CANVAS_VERSION),
75 cur_time_ (0),
76 is_inline_ (false),
77 is_dirty_ (true),
78 op_flag_ (false)
80 _CanvasCounter::counter++;
81 clear();
84 void
85 Canvas::on_changed()
87 is_dirty_=true;
88 Node::on_changed();
91 Canvas::~Canvas()
93 // we were having a crash where pastecanvas layers were still
94 // refering to a canvas after it had been destroyed; this code
95 // will stop the pastecanvas layers from refering to the canvas
96 // before the canvas is destroyed
98 // the set_sub_canvas(0) ends up deleting the parent-child link,
99 // which deletes the current element from the set we're iterating
100 // through, so we have to make sure we've incremented the iterator
101 // before we mess with the pastecanvas
102 std::set<Node*>::iterator iter = parent_set.begin();
103 while (iter != parent_set.end())
105 Layer_PasteCanvas* paste_canvas = dynamic_cast<Layer_PasteCanvas*>(*iter);
106 iter++;
107 if(paste_canvas)
108 paste_canvas->set_sub_canvas(0);
109 else
110 warning("destroyed canvas has a parent that is not a pastecanvas - please report if repeatable");
113 //if(is_inline() && parent_) assert(0);
114 _CanvasCounter::counter--;
115 clear();
116 begin_delete();
119 Canvas::iterator
120 Canvas::end()
122 return CanvasBase::end()-1;
125 Canvas::const_iterator
126 Canvas::end()const
128 return CanvasBase::end()-1;
131 Canvas::reverse_iterator
132 Canvas::rbegin()
134 return CanvasBase::rbegin()+1;
137 Canvas::const_reverse_iterator
138 Canvas::rbegin()const
140 return CanvasBase::rbegin()+1;
144 Canvas::size()const
146 return CanvasBase::size()-1;
149 void
150 Canvas::clear()
152 while(!empty())
154 Layer::Handle layer(front());
155 //if(layer->count()>2)synfig::info("before layer->count()=%d",layer->count());
157 erase(begin());
158 //if(layer->count()>1)synfig::info("after layer->count()=%d",layer->count());
160 //CanvasBase::clear();
162 // We need to keep a blank handle at the
163 // end of the image list, and acts at
164 // the bottom. Without it, the layers
165 // would just continue going when polled
166 // for a color.
167 CanvasBase::push_back(Layer::Handle());
169 changed();
172 bool
173 Canvas::empty()const
175 return CanvasBase::size()<=1;
178 Layer::Handle &
179 Canvas::back()
181 return *(CanvasBase::end()-1);
184 const Layer::Handle &
185 Canvas::back()const
187 return *(CanvasBase::end()-1);
190 Context
191 Canvas::get_context()const
193 return begin();
196 const ValueNodeList &
197 Canvas::value_node_list()const
199 if(is_inline() && parent_)
200 return parent_->value_node_list();
201 return value_node_list_;
204 KeyframeList &
205 Canvas::keyframe_list()
207 if(is_inline() && parent_)
208 return parent_->keyframe_list();
209 return keyframe_list_;
212 const KeyframeList &
213 Canvas::keyframe_list()const
215 if(is_inline() && parent_)
216 return parent_->keyframe_list();
217 return keyframe_list_;
220 etl::handle<Layer>
221 Canvas::find_layer(const Point &pos)
223 return get_context().hit_check(pos);
226 static bool
227 valid_id(const String &x)
229 static const char bad_chars[]=" :#@$^&()*";
230 unsigned int i;
232 if(!x.empty() && x[0]>='0' && x[0]<='9')
233 return false;
235 for(i=0;i<sizeof(bad_chars);i++)
236 if(x.find_first_of(bad_chars[i])!=string::npos)
237 return false;
239 return true;
242 void
243 Canvas::set_id(const String &x)
245 if(is_inline() && parent_)
246 throw runtime_error("Inline Canvas cannot have an ID");
248 if(!valid_id(x))
249 throw runtime_error("Invalid ID");
250 id_=x;
251 signal_id_changed_();
254 void
255 Canvas::set_name(const String &x)
257 name_=x;
258 signal_meta_data_changed()("name");
259 signal_meta_data_changed("name")();
262 void
263 Canvas::set_author(const String &x)
265 author_=x;
266 signal_meta_data_changed()("author");
267 signal_meta_data_changed("author")();
270 void
271 Canvas::set_description(const String &x)
273 description_=x;
274 signal_meta_data_changed()("description");
275 signal_meta_data_changed("description")();
278 void
279 Canvas::set_time(Time t)const
281 if(is_dirty_ || !get_time().is_equal(t))
283 #if 0
284 if(is_root())
286 synfig::info("is_dirty_=%d",is_dirty_);
287 synfig::info("get_time()=%f",(float)get_time());
288 synfig::info("t=%f",(float)t);
290 #endif
292 // ...questionable
293 const_cast<Canvas&>(*this).cur_time_=t;
295 is_dirty_=false;
296 get_context().set_time(t);
298 is_dirty_=false;
301 Canvas::LooseHandle
302 Canvas::get_root()const
304 return parent_?parent_->get_root().get():const_cast<synfig::Canvas *>(this);
308 Canvas::get_depth(etl::handle<Layer> layer)const
310 const_iterator iter;
311 int i(0);
312 for(iter=begin();iter!=end();++iter,i++)
314 if(layer==*iter)
315 return i;
317 return -1;
320 String
321 Canvas::get_relative_id(etl::loose_handle<const Canvas> x)const
323 if(x->get_root()==this)
324 return ":";
325 if(is_inline() && parent_)
326 return parent_->_get_relative_id(x);
327 return _get_relative_id(x);
330 String
331 Canvas::_get_relative_id(etl::loose_handle<const Canvas> x)const
333 if(is_inline() && parent_)
334 return parent_->_get_relative_id(x);
336 if(x.get()==this)
337 return String();
339 if(parent()==x.get())
340 return get_id();
342 String id;
344 const Canvas* canvas=this;
346 for(;!canvas->is_root();canvas=canvas->parent().get())
347 id=':'+canvas->get_id()+id;
349 if(x && get_root()!=x->get_root())
351 //String file_name=get_file_name();
352 //String file_path=x->get_file_path();
354 String file_name;
355 if(is_absolute_path(get_file_name()))
356 file_name=etl::relative_path(x->get_file_path(),get_file_name());
357 else
358 file_name=get_file_name();
360 // If the path of X is inside of file_name,
361 // then remove it.
362 //if(file_name.size()>file_path.size())
363 // if(file_path==String(file_name,0,file_path.size()))
364 // file_name.erase(0,file_path.size()+1);
366 id=file_name+'#'+id;
369 return id;
372 ValueNode::Handle
373 Canvas::find_value_node(const String &id)
375 return
376 ValueNode::Handle::cast_const(
377 const_cast<const Canvas*>(this)->find_value_node(id)
381 ValueNode::ConstHandle
382 Canvas::find_value_node(const String &id)const
384 if(is_inline() && parent_)
385 return parent_->find_value_node(id);
387 if(id.empty())
388 throw Exception::IDNotFound("Empty ID");
390 // If we do not have any resolution, then we assume that the
391 // request is for this immediate canvas
392 if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
393 return value_node_list_.find(id);
395 String canvas_id(id,0,id.rfind(':'));
396 String value_node_id(id,id.rfind(':')+1);
397 if(canvas_id.empty())
398 canvas_id=':';
399 //synfig::warning("constfind:value_node_id: "+value_node_id);
400 //synfig::warning("constfind:canvas_id: "+canvas_id);
402 String warnings;
403 return find_canvas(canvas_id, warnings)->value_node_list_.find(value_node_id);
406 ValueNode::Handle
407 Canvas::surefind_value_node(const String &id)
409 if(is_inline() && parent_)
410 return parent_->surefind_value_node(id);
412 if(id.empty())
413 throw Exception::IDNotFound("Empty ID");
415 // If we do not have any resolution, then we assume that the
416 // request is for this immediate canvas
417 if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
418 return value_node_list_.surefind(id);
420 String canvas_id(id,0,id.rfind(':'));
421 String value_node_id(id,id.rfind(':')+1);
422 if(canvas_id.empty())
423 canvas_id=':';
425 String warnings;
426 return surefind_canvas(canvas_id,warnings)->value_node_list_.surefind(value_node_id);
429 void
430 Canvas::add_value_node(ValueNode::Handle x, const String &id)
432 if(is_inline() && parent_)
433 return parent_->add_value_node(x,id);
434 // throw runtime_error("You cannot add a ValueNode to an inline Canvas");
436 if(x->is_exported())
437 throw runtime_error("ValueNode is already exported");
439 if(id.empty())
440 throw Exception::BadLinkName("Empty ID");
442 if(id.find_first_of(':',0)!=string::npos)
443 throw Exception::BadLinkName("Bad character");
447 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
448 throw Exception::IDNotFound("add_value_node()");
450 throw Exception::IDAlreadyExists(id);
452 catch(Exception::IDNotFound)
454 x->set_id(id);
456 x->set_parent_canvas(this);
458 if(!value_node_list_.add(x))
460 synfig::error("Unable to add ValueNode");
461 throw std::runtime_error("Unable to add ValueNode");
464 return;
469 void
470 Canvas::rename_value_node(ValueNode::Handle x, const String &id)
472 if(id.empty())
473 throw Exception::BadLinkName("Empty ID");
475 if(id.find_first_of(": ",0)!=string::npos)
476 throw Exception::BadLinkName("Bad character");
480 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
481 throw Exception::IDNotFound("rename_value_node");
482 throw Exception::IDAlreadyExists(id);
484 catch(Exception::IDNotFound)
486 x->set_id(id);
488 return;
493 void
494 Canvas::remove_value_node(ValueNode::Handle x)
496 if(is_inline() && parent_)
497 return parent_->remove_value_node(x);
498 // throw Exception::IDNotFound("Canvas::remove_value_node() was called from an inline canvas");
500 if(!x)
501 throw Exception::IDNotFound("Canvas::remove_value_node() was passed empty handle");
503 if(!value_node_list_.erase(x))
504 throw Exception::IDNotFound("Canvas::remove_value_node(): ValueNode was not found inside of this canvas");
506 //x->set_parent_canvas(0);
508 x->set_id("");
511 Canvas::Handle
512 Canvas::surefind_canvas(const String &id, String &warnings)
514 if(is_inline() && parent_)
515 return parent_->surefind_canvas(id,warnings);
517 if(id.empty())
518 return this;
520 // If the ID contains a "#" character, then a filename is
521 // expected on the left side.
522 if(id.find_first_of('#')!=string::npos)
524 // If '#' is the first character, remove it
525 // and attempt to parse the ID again.
526 if(id[0]=='#')
527 return surefind_canvas(String(id,1),warnings);
529 //! \todo This needs a lot more optimization
530 String file_name(id,0,id.find_first_of('#'));
531 String external_id(id,id.find_first_of('#')+1);
533 file_name=unix_to_local_path(file_name);
535 Canvas::Handle external_canvas;
537 // If the composition is already open, then use it.
538 if(externals_.count(file_name))
539 external_canvas=externals_[file_name];
540 else
542 String errors;
543 if(is_absolute_path(file_name))
544 external_canvas=open_canvas(file_name, errors, warnings);
545 else
546 external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name, errors, warnings);
548 if(!external_canvas)
549 throw runtime_error(errors);
550 externals_[file_name]=external_canvas;
553 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id, warnings));
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)
560 Children::iterator iter;
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;
568 // Create a new canvas and return it
569 //synfig::warning("Implicitly creating canvas named "+id);
570 return new_child_canvas(id);
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);
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.
582 String canvas_name=string(id,0,id.find_first_of(':'));
584 Canvas::Handle child_canvas=surefind_canvas(canvas_name,warnings);
586 return child_canvas->surefind_canvas(string(id,id.find_first_of(':')+1),warnings);
589 Canvas::Handle
590 Canvas::find_canvas(const String &id, String &warnings)
592 return
593 Canvas::Handle::cast_const(
594 const_cast<const Canvas*>(this)->find_canvas(id, warnings)
598 Canvas::ConstHandle
599 Canvas::find_canvas(const String &id, String &warnings)const
601 if(is_inline() && parent_)
602 return parent_->find_canvas(id, warnings);
604 if(id.empty())
605 return this;
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)
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);
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);
620 file_name=unix_to_local_path(file_name);
622 Canvas::Handle external_canvas;
624 // If the composition is already open, then use it.
625 if(externals_.count(file_name))
626 external_canvas=externals_[file_name];
627 else
629 String errors, warnings;
630 if(is_absolute_path(file_name))
631 external_canvas=open_canvas(file_name, errors, warnings);
632 else
633 external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPARATOR+file_name, errors, warnings);
635 if(!external_canvas)
636 throw runtime_error(errors);
637 externals_[file_name]=external_canvas;
640 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id, warnings));
643 // If we do not have any resolution, then we assume that the
644 // request is for this immediate canvas
645 if(id.find_first_of(':')==string::npos)
647 Children::const_iterator iter;
649 // Search for the image in the image list,
650 // and return it if it is found
651 for(iter=children().begin();iter!=children().end();iter++)
652 if(id==(*iter)->get_id())
653 return *iter;
655 throw Exception::IDNotFound("Child Canvas in Parent Canvas: (child)"+id);
658 // If the first character is the separator, then
659 // this references the root canvas.
660 if(id[0]==':')
661 return get_root()->find_canvas(string(id,1), warnings);
663 // Now we know that the requested Canvas is in a child
664 // of this canvas. We have to find that canvas and
665 // call "find_canvas" on it, and return the result.
667 String canvas_name=string(id,0,id.find_first_of(':'));
669 Canvas::ConstHandle child_canvas=find_canvas(canvas_name, warnings);
671 return child_canvas->find_canvas(string(id,id.find_first_of(':')+1), warnings);
674 Canvas::Handle
675 Canvas::create()
677 return new Canvas("Untitled");
680 void
681 Canvas::push_back(etl::handle<Layer> x)
683 // int i(x->count());
684 insert(end(),x);
685 //if(x->count()!=i+1)synfig::info("push_back before %d, after %d",i,x->count());
688 void
689 Canvas::push_front(etl::handle<Layer> x)
691 // int i(x->count());
692 insert(begin(),x);
693 //if(x->count()!=i+1)synfig::error("push_front before %d, after %d",i,x->count());
696 void
697 Canvas::insert(iterator iter,etl::handle<Layer> x)
699 // int i(x->count());
700 CanvasBase::insert(iter,x);
702 /*if(x->count()!=i+1)
704 synfig::error(__FILE__":%d: Canvas::insert(): ***FAILURE*** before %d, after %d",__LINE__,i,x->count());
705 return;
706 //throw runtime_error("Canvas Insertion Failed");
709 x->set_canvas(this);
711 add_child(x.get());
713 LooseHandle correct_canvas(this);
714 //while(correct_canvas->is_inline())correct_canvas=correct_canvas->parent();
715 Layer::LooseHandle loose_layer(x);
717 add_connection(loose_layer,
718 sigc::connection::connection(
719 x->signal_added_to_group().connect(
720 sigc::bind(
721 sigc::mem_fun(
722 *correct_canvas,
723 &Canvas::add_group_pair),
724 loose_layer))));
725 add_connection(loose_layer,
726 sigc::connection::connection(
727 x->signal_removed_from_group().connect(
728 sigc::bind(
729 sigc::mem_fun(
730 *correct_canvas,
731 &Canvas::remove_group_pair),
732 loose_layer))));
734 if(!x->get_group().empty())
735 add_group_pair(x->get_group(),x);
737 changed();
740 void
741 Canvas::push_back_simple(etl::handle<Layer> x)
743 CanvasBase::insert(end(),x);
744 changed();
747 void
748 Canvas::erase(iterator iter)
750 if(!(*iter)->get_group().empty())
751 remove_group_pair((*iter)->get_group(),(*iter));
753 // HACK: We really shouldn't be wiping
754 // out these signals entirely. We should
755 // only be removing the specific connections
756 // that we made. At the moment, I'm too
757 // lazy to add the code to keep track
758 // of those connections, and no one else
759 // is using these signals, so I'll just
760 // leave these next two lines like they
761 // are for now - darco 07-30-2004
763 // so don't wipe them out entirely
764 // - dooglus 09-21-2007
765 disconnect_connections(*iter);
767 if(!op_flag_)remove_child(iter->get());
769 CanvasBase::erase(iter);
770 if(!op_flag_)changed();
773 Canvas::Handle
774 Canvas::clone(const GUID& deriv_guid)const
776 synfig::String name;
777 if(is_inline())
778 name="inline";
779 else
781 name=get_id()+"_CLONE";
783 throw runtime_error("Cloning of non-inline canvases is not yet supported");
786 Handle canvas(new Canvas(name));
788 if(is_inline())
790 canvas->is_inline_=true;
791 // \todo this was setting parent_=0 - is there a reason for that?
792 // this was causing bug 1838132, where cloning an inline canvas that contains an imported image fails
793 // it was failing to ascertain the absolute pathname of the imported image, since it needs the pathname
794 // of the canvas to get that, which is stored in the parent canvas
795 canvas->parent_=parent();
796 canvas->rend_desc() = rend_desc();
797 //canvas->set_inline(parent());
800 canvas->set_guid(get_guid()^deriv_guid);
802 const_iterator iter;
803 for(iter=begin();iter!=end();++iter)
805 Layer::Handle layer((*iter)->clone(deriv_guid));
806 if(layer)
808 assert(layer.count()==1);
809 int presize(size());
810 canvas->push_back(layer);
811 if(!(layer.count()>1))
813 synfig::error("Canvas::clone(): Cloned layer insertion failure!");
814 synfig::error("Canvas::clone(): \tlayer.count()=%d",layer.count());
815 synfig::error("Canvas::clone(): \tlayer->get_name()=%s",layer->get_name().c_str());
816 synfig::error("Canvas::clone(): \tbefore size()=%d",presize);
817 synfig::error("Canvas::clone(): \tafter size()=%d",size());
819 assert(layer.count()>1);
821 else
823 synfig::error("Unable to clone layer");
827 canvas->signal_group_pair_removed().clear();
828 canvas->signal_group_pair_added().clear();
830 return canvas;
833 void
834 Canvas::set_inline(LooseHandle parent)
836 if(is_inline_ && parent_)
841 id_="inline";
842 is_inline_=true;
843 parent_=parent;
845 // Have the parent inherit all of the group stuff
847 std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
849 for(iter=group_db_.begin();iter!=group_db_.end();++iter)
851 parent->group_db_[iter->first].insert(iter->second.begin(),iter->second.end());
854 rend_desc()=parent->rend_desc();
857 Canvas::Handle
858 Canvas::create_inline(Handle parent)
860 assert(parent);
861 //if(parent->is_inline())
862 // return create_inline(parent->parent());
864 Handle canvas(new Canvas("inline"));
865 canvas->set_inline(parent);
866 return canvas;
869 Canvas::Handle
870 Canvas::new_child_canvas()
872 if(is_inline() && parent_)
873 return parent_->new_child_canvas();
874 // runtime_error("You cannot create a child Canvas in an inline Canvas");
876 // Create a new canvas
877 children().push_back(create());
878 Canvas::Handle canvas(children().back());
880 canvas->parent_=this;
882 canvas->rend_desc()=rend_desc();
884 return canvas;
887 Canvas::Handle
888 Canvas::new_child_canvas(const String &id)
890 if(is_inline() && parent_)
891 return parent_->new_child_canvas(id);
892 // runtime_error("You cannot create a child Canvas in an inline Canvas");
894 // Create a new canvas
895 children().push_back(create());
896 Canvas::Handle canvas(children().back());
898 canvas->set_id(id);
899 canvas->parent_=this;
900 canvas->rend_desc()=rend_desc();
902 return canvas;
905 Canvas::Handle
906 Canvas::add_child_canvas(Canvas::Handle child_canvas, const synfig::String& id)
908 if(is_inline() && parent_)
909 return parent_->add_child_canvas(child_canvas,id);
911 if(child_canvas->parent() && !child_canvas->is_inline())
912 throw std::runtime_error("Cannot add child canvas because it belongs to someone else!");
914 if(!valid_id(id))
915 throw runtime_error("Invalid ID");
919 String warnings;
920 find_canvas(id, warnings);
921 throw Exception::IDAlreadyExists(id);
923 catch(Exception::IDNotFound)
925 if(child_canvas->is_inline())
926 child_canvas->is_inline_=false;
927 child_canvas->id_=id;
928 children().push_back(child_canvas);
929 child_canvas->parent_=this;
932 return child_canvas;
935 void
936 Canvas::remove_child_canvas(Canvas::Handle child_canvas)
938 if(is_inline() && parent_)
939 return parent_->remove_child_canvas(child_canvas);
941 if(child_canvas->parent_!=this)
942 throw runtime_error("Given child does not belong to me");
944 if(find(children().begin(),children().end(),child_canvas)==children().end())
945 throw Exception::IDNotFound(child_canvas->get_id());
947 children().remove(child_canvas);
949 child_canvas->parent_=0;
952 void
953 Canvas::set_file_name(const String &file_name)
955 if(parent())
956 parent()->set_file_name(file_name);
957 else
959 file_name_=file_name;
960 signal_file_name_changed_();
964 sigc::signal<void>&
965 Canvas::signal_file_name_changed()
967 if(parent())
968 return signal_file_name_changed();
969 else
970 return signal_file_name_changed_;
973 String
974 Canvas::get_file_name()const
976 if(parent())
977 return parent()->get_file_name();
978 return file_name_;
981 String
982 Canvas::get_file_path()const
984 if(parent())
985 return parent()->get_file_path();
986 return dirname(file_name_);
989 String
990 Canvas::get_meta_data(const String& key)const
992 if(!meta_data_.count(key))
993 return String();
994 return meta_data_.find(key)->second;
997 void
998 Canvas::set_meta_data(const String& key, const String& data)
1000 if(meta_data_[key]!=data)
1002 meta_data_[key]=data;
1003 signal_meta_data_changed()(key);
1004 signal_meta_data_changed(key)();
1008 void
1009 Canvas::erase_meta_data(const String& key)
1011 if(meta_data_.count(key))
1013 meta_data_.erase(key);
1014 signal_meta_data_changed()(key);
1015 signal_meta_data_changed(key)();
1019 std::list<String>
1020 Canvas::get_meta_data_keys()const
1022 std::list<String> ret;
1024 std::map<String,String>::const_iterator iter;
1026 for(iter=meta_data_.begin();!(iter==meta_data_.end());++iter)
1027 ret.push_back(iter->first);
1029 return ret;
1032 /* note - the "Motion Blur" and "Duplicate" layers need the dynamic
1033 parameters of any PasteCanvas layers they loop over to be
1034 maintained. When the variables in the following function
1035 refer to "motion blur", they mean either of these two
1036 layers. */
1037 void
1038 synfig::optimize_layers(Time time, Context context, Canvas::Handle op_canvas, bool seen_motion_blur_in_parent)
1040 Context iter;
1042 std::vector< std::pair<float,Layer::Handle> > sort_list;
1043 int i, motion_blur_i=0; // motion_blur_i is for resolving which layer comes first in the event of a z_depth tie
1044 float motion_blur_z_depth=0; // the z_depth of the least deep motion blur layer in this context
1045 bool seen_motion_blur_locally = false;
1046 bool motion_blurred; // the final result - is this layer blurred or not?
1048 // If the parent didn't cause us to already be motion blurred,
1049 // check whether there's a motion blur in this context,
1050 // and if so, calculate its z_depth.
1051 if (!seen_motion_blur_in_parent)
1052 for(iter=context,i=0;*iter;iter++,i++)
1054 Layer::Handle layer=*iter;
1056 // If the layer isn't active, don't worry about it
1057 if(!layer->active())
1058 continue;
1060 // Any layer with an amount of zero is implicitly disabled.
1061 ValueBase value(layer->get_param("amount"));
1062 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1063 continue;
1065 if(layer->get_name()=="MotionBlur" || layer->get_name()=="duplicate")
1067 float z_depth(layer->get_z_depth()*1.0001+i);
1069 // If we've seen a motion blur before in this context...
1070 if (seen_motion_blur_locally)
1072 // ... then we're only interested in this one if it's less deep...
1073 if (z_depth < motion_blur_z_depth)
1075 motion_blur_z_depth = z_depth;
1076 motion_blur_i = i;
1079 // ... otherwise we're always interested in it.
1080 else
1082 motion_blur_z_depth = z_depth;
1083 motion_blur_i = i;
1084 seen_motion_blur_locally = true;
1089 // Go ahead and start romping through the canvas to paste
1090 for(iter=context,i=0;*iter;iter++,i++)
1092 Layer::Handle layer=*iter;
1093 float z_depth(layer->get_z_depth()*1.0001+i);
1095 // If the layer isn't active, don't worry about it
1096 if(!layer->active())
1097 continue;
1099 // Any layer with an amount of zero is implicitly disabled.
1100 ValueBase value(layer->get_param("amount"));
1101 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1102 continue;
1104 // note: this used to include "&& paste_canvas->get_time_offset()==0", but then
1105 // time-shifted layers weren't being sorted by z-depth (bug #1806852)
1106 if(layer->get_name()=="PasteCanvas")
1108 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1110 // we need to blur the sub canvas if:
1111 // our parent is blurred,
1112 // or the child is lower than a local blur,
1113 // or the child is at the same z_depth as a local blur, but later in the context
1115 #if 0 // DEBUG
1116 if (seen_motion_blur_in_parent) synfig::info("seen BLUR in parent\n");
1117 else if (seen_motion_blur_locally)
1118 if (z_depth > motion_blur_z_depth) synfig::info("paste is deeper than BLUR\n");
1119 else if (z_depth == motion_blur_z_depth) { synfig::info("paste is same depth as BLUR\n");
1120 if (i > motion_blur_i) synfig::info("paste is physically deeper than BLUR\n");
1121 else synfig::info("paste is less physically deep than BLUR\n");
1122 } else synfig::info("paste is less deep than BLUR\n");
1123 else synfig::info("no BLUR at all\n");
1124 #endif // DEBUG
1126 motion_blurred = (seen_motion_blur_in_parent ||
1127 (seen_motion_blur_locally &&
1128 (z_depth > motion_blur_z_depth ||
1129 (z_depth == motion_blur_z_depth && i > motion_blur_i))));
1131 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1132 Canvas::Handle paste_sub_canvas = paste_canvas->get_sub_canvas();
1133 if(paste_sub_canvas)
1134 optimize_layers(time, paste_sub_canvas->get_context(),sub_canvas,motion_blurred);
1136 // \todo: uncommenting the following breaks the rendering of at least examples/backdrop.sifz quite severely
1137 // #define SYNFIG_OPTIMIZE_PASTE_CANVAS
1138 #ifdef SYNFIG_OPTIMIZE_PASTE_CANVAS
1139 Canvas::iterator sub_iter;
1141 // Determine if we can just remove the paste canvas altogether
1142 if (paste_canvas->get_blend_method() == Color::BLEND_COMPOSITE &&
1143 paste_canvas->get_amount() == 1.0f &&
1144 paste_canvas->get_zoom() == 0 &&
1145 paste_canvas->get_time_offset() == 0 &&
1146 paste_canvas->get_origin() == Point(0,0) )
1147 try {
1148 for(sub_iter=sub_canvas->begin();sub_iter!=sub_canvas->end();++sub_iter)
1150 Layer* layer=sub_iter->get();
1152 // any layers that deform end up breaking things
1153 // so do things the old way if we run into anything like this
1154 if(!dynamic_cast<Layer_NoDeform*>(layer))
1155 throw int();
1157 ValueBase value(layer->get_param("blend_method"));
1158 if(value.get_type()!=ValueBase::TYPE_INTEGER || value.get(int())!=(int)Color::BLEND_COMPOSITE)
1159 throw int();
1162 // It has turned out that we don't need a paste canvas
1163 // layer, so just go ahead and add all the layers onto
1164 // the current stack and be done with it
1165 while(sub_canvas->size())
1167 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,sub_canvas->front()));
1168 //op_canvas->push_back_simple(sub_canvas->front());
1169 sub_canvas->pop_front();
1171 continue;
1173 catch(int)
1175 #endif // SYNFIG_OPTIMIZE_PASTE_CANVAS
1177 Layer::Handle new_layer(Layer::create("PasteCanvas"));
1178 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(false);
1179 if (motion_blurred)
1181 Layer::DynamicParamList dynamic_param_list(paste_canvas->dynamic_param_list());
1182 for(Layer::DynamicParamList::const_iterator iter(dynamic_param_list.begin()); iter != dynamic_param_list.end(); ++iter)
1183 new_layer->connect_dynamic_param(iter->first, iter->second);
1185 Layer::ParamList param_list(paste_canvas->get_param_list());
1186 //param_list.erase("canvas");
1187 new_layer->set_param_list(param_list);
1188 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_sub_canvas(sub_canvas);
1189 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_muck_with_time(true);
1190 layer=new_layer;
1192 else // not a PasteCanvas - does it use blend method 'Straight'?
1194 /* when we use the 'straight' blend method, every pixel on the layer affects the layers underneath,
1195 * not just the non-transparent pixels; the following workarea wraps non-pastecanvas layers in a
1196 * new pastecanvas to ensure that the straight blend affects the full plane, not just the area
1197 * within the layer's bounding box
1200 // \todo: this code probably needs modification to work properly with motionblur and duplicate
1201 etl::handle<Layer_Composite> composite = etl::handle<Layer_Composite>::cast_dynamic(layer);
1203 /* some layers (such as circle) don't touch pixels that aren't
1204 * part of the circle, so they don't get blended correctly when
1205 * using a straight blend. so we encapsulate the circle, and the
1206 * encapsulation layer takes care of the transparent pixels
1207 * for us. if we do that for all layers, however, then the
1208 * distortion layers no longer work, since they have no
1209 * context to work on. the Layer::reads_context() method
1210 * returns true for layers which need to be able to see
1211 * their context. we can't encapsulate those.
1213 if (composite &&
1214 Color::is_straight(composite->get_blend_method()) &&
1215 !composite->reads_context())
1217 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1218 sub_canvas->push_back(composite = composite->clone());
1219 layer = Layer::create("PasteCanvas");
1220 composite->set_description(strprintf("Wrapped clone of '%s'", composite->get_non_empty_description().c_str()));
1221 layer->set_description(strprintf("PasteCanvas wrapper for '%s'", composite->get_non_empty_description().c_str()));
1222 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1223 paste_canvas->set_blend_method(composite->get_blend_method());
1224 paste_canvas->set_amount(composite->get_amount());
1225 sub_canvas->set_time(time); // region and outline don't calculate their bounding rects until their time is set
1226 composite->set_blend_method(Color::BLEND_STRAIGHT); // do this before calling set_sub_canvas(), but after set_time()
1227 composite->set_amount(1.0f); // after set_time()
1228 paste_canvas->set_sub_canvas(sub_canvas);
1232 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,layer));
1233 //op_canvas->push_back_simple(layer);
1236 //sort_list.sort();
1237 stable_sort(sort_list.begin(),sort_list.end());
1238 std::vector< std::pair<float,Layer::Handle> >::iterator iter2;
1239 for(iter2=sort_list.begin();iter2!=sort_list.end();++iter2)
1240 op_canvas->push_back_simple(iter2->second);
1241 op_canvas->op_flag_=true;
1244 void
1245 Canvas::get_times_vfunc(Node::time_set &set) const
1247 const_iterator i = begin(),
1248 iend = end();
1250 for(; i != iend; ++i)
1252 const Node::time_set &tset = (*i)->get_times();
1253 set.insert(tset.begin(),tset.end());
1257 std::set<etl::handle<Layer> >
1258 Canvas::get_layers_in_group(const String&group)
1260 if(is_inline() && parent_)
1261 return parent_->get_layers_in_group(group);
1263 if(group_db_.count(group)==0)
1264 return std::set<etl::handle<Layer> >();
1265 return group_db_.find(group)->second;
1268 std::set<String>
1269 Canvas::get_groups()const
1271 if(is_inline() && parent_)
1272 return parent_->get_groups();
1274 std::set<String> ret;
1275 std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
1276 for(iter=group_db_.begin();iter!=group_db_.end();++iter)
1277 ret.insert(iter->first);
1278 return ret;
1282 Canvas::get_group_count()const
1284 if(is_inline() && parent_)
1285 return parent_->get_group_count();
1287 return group_db_.size();
1290 void
1291 Canvas::add_group_pair(String group, etl::handle<Layer> layer)
1293 group_db_[group].insert(layer);
1294 if(group_db_[group].size()==1)
1295 signal_group_added()(group);
1296 else
1297 signal_group_changed()(group);
1299 signal_group_pair_added()(group,layer);
1301 if(is_inline() && parent_)
1302 return parent_->add_group_pair(group,layer);
1305 void
1306 Canvas::remove_group_pair(String group, etl::handle<Layer> layer)
1308 group_db_[group].erase(layer);
1310 signal_group_pair_removed()(group,layer);
1312 if(group_db_[group].empty())
1314 group_db_.erase(group);
1315 signal_group_removed()(group);
1317 else
1318 signal_group_changed()(group);
1320 if(is_inline() && parent_)
1321 return parent_->remove_group_pair(group,layer);
1324 void
1325 Canvas::add_connection(etl::loose_handle<Layer> layer, sigc::connection connection)
1327 connections_[layer].push_back(connection);
1330 void
1331 Canvas::disconnect_connections(etl::loose_handle<Layer> layer)
1333 std::vector<sigc::connection>::iterator iter;
1334 for(iter=connections_[layer].begin();iter!=connections_[layer].end();++iter)
1335 iter->disconnect();
1336 connections_[layer].clear();
1339 void
1340 Canvas::rename_group(const String&old_name,const String&new_name)
1342 if(is_inline() && parent_)
1343 return parent_->rename_group(old_name,new_name);
1346 std::map<String,std::set<etl::handle<Layer> > >::iterator iter;
1347 iter=group_db_.find(old_name);
1348 if(iter!=group_db_.end())
1349 for(++iter;iter!=group_db_.end() && iter->first.find(old_name)==0;iter=group_db_.find(old_name),++iter)
1351 String name(iter->first,old_name.size(),String::npos);
1352 name=new_name+name;
1353 rename_group(iter->first,name);
1357 std::set<etl::handle<Layer> > layers(get_layers_in_group(old_name));
1358 std::set<etl::handle<Layer> >::iterator iter;
1360 for(iter=layers.begin();iter!=layers.end();++iter)
1362 (*iter)->remove_from_group(old_name);
1363 (*iter)->add_to_group(new_name);