1 /* === S Y N F I G ========================================================= */
3 ** \brief Canvas Class Member Definitions
5 ** $Id: canvas.cpp,v 1.1.1.1 2005/01/04 01:23:14 darco Exp $
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
10 ** This package is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU General Public License as
12 ** published by the Free Software Foundation; either version 2 of
13 ** the License, or (at your option) any later version.
15 ** This package is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ** General Public License for more details.
21 /* ========================================================================= */
23 /* === H E A D E R S ======================================================= */
25 #define SYNFIG_NO_ANGLE
37 #include "exception.h"
40 #include "layer_pastecanvas.h"
41 #include <sigc++/bind.h>
45 using namespace synfig;
49 namespace synfig { extern Canvas::Handle open_canvas(const String &filename); };
51 /* === M A C R O S ========================================================= */
59 synfig::error("%d canvases not yet deleted!",counter);
63 int _CanvasCounter::counter(0);
65 /* === G L O B A L S ======================================================= */
67 /* === P R O C E D U R E S ================================================= */
69 /* === M E T H O D S ======================================================= */
71 Canvas::Canvas(const string &id):
78 _CanvasCounter::counter++;
91 //if(is_inline() && parent_) assert(0);
92 _CanvasCounter::counter--;
101 return CanvasBase::end()-1;
104 Canvas::const_iterator
107 return CanvasBase::end()-1;
110 Canvas::reverse_iterator
113 return CanvasBase::rbegin()+1;
116 Canvas::const_reverse_iterator
117 Canvas::rbegin()const
119 return CanvasBase::rbegin()+1;
125 return CanvasBase::size()-1;
133 Layer::Handle layer(front());
134 //if(layer->count()>2)synfig::info("before layer->count()=%d",layer->count());
137 //if(layer->count()>1)synfig::info("after layer->count()=%d",layer->count());
139 //CanvasBase::clear();
141 // We need to keep a blank handle at the
142 // end of the image list, and acts at
143 // the bottom. Without it, the layers
144 // would just continue going when polled
146 CanvasBase::push_back(Layer::Handle());
154 return CanvasBase::size()<=1;
160 return *(CanvasBase::end()-1);
163 const Layer::Handle &
166 return *(CanvasBase::end()-1);
170 Canvas::get_context()const
175 const ValueNodeList &
176 Canvas::value_node_list()const
178 if(is_inline() && parent_)
179 return parent_->value_node_list();
180 return value_node_list_;
184 Canvas::keyframe_list()
186 if(is_inline() && parent_)
187 return parent_->keyframe_list();
188 return keyframe_list_;
192 Canvas::keyframe_list()const
194 if(is_inline() && parent_)
195 return parent_->keyframe_list();
196 return keyframe_list_;
200 Canvas::find_layer(const Point &pos)
202 return get_context().hit_check(pos);
206 valid_id(const String &x)
208 static const char bad_chars[]=" :#@$^&()*";
211 if(!x.empty() && x[0]>='0' && x[0]<='9')
214 for(i=0;i<sizeof(bad_chars);i++)
215 if(x.find_first_of(bad_chars[i])!=string::npos)
222 Canvas::set_id(const String &x)
224 if(is_inline() && parent_)
225 throw runtime_error("Inline Canvas cannot have an ID");
228 throw runtime_error("Invalid ID");
230 signal_id_changed_();
234 Canvas::set_name(const String &x)
237 signal_meta_data_changed()("name");
238 signal_meta_data_changed("name")();
242 Canvas::set_author(const String &x)
245 signal_meta_data_changed()("author");
246 signal_meta_data_changed("author")();
250 Canvas::set_description(const String &x)
253 signal_meta_data_changed()("description");
254 signal_meta_data_changed("description")();
258 Canvas::set_time(Time t)const
260 if(is_dirty_ || !get_time().is_equal(t))
265 synfig::info("is_dirty_=%d",is_dirty_);
266 synfig::info("get_time()=%f",(float)get_time());
267 synfig::info("t=%f",(float)t);
272 const_cast<Canvas&>(*this).cur_time_=t;
275 get_context().set_time(t);
281 Canvas::get_root()const
283 return parent_?parent_->get_root().get():const_cast<synfig::Canvas *>(this);
287 Canvas::get_depth(etl::handle<Layer> layer)const
291 for(iter=begin();iter!=end();++iter,i++)
300 Canvas::get_relative_id(etl::loose_handle<const Canvas> x)const
302 if(x->get_root()==this)
304 if(is_inline() && parent_)
305 return parent_->_get_relative_id(x);
306 return _get_relative_id(x);
310 Canvas::_get_relative_id(etl::loose_handle<const Canvas> x)const
312 if(is_inline() && parent_)
313 return parent_->_get_relative_id(x);
318 if(parent()==x.get())
323 const Canvas* canvas=this;
325 for(;!canvas->is_root();canvas=canvas->parent().get())
326 id=':'+canvas->get_id()+id;
328 if(x && get_root()!=x->get_root())
330 //String file_name=get_file_name();
331 //String file_path=x->get_file_path();
334 if(is_absolute_path(get_file_name()))
335 file_name=etl::relative_path(x->get_file_path(),get_file_name());
337 file_name=get_file_name();
339 // If the path of X is inside of file_name,
341 //if(file_name.size()>file_path.size())
342 // if(file_path==String(file_name,0,file_path.size()))
343 // file_name.erase(0,file_path.size()+1);
353 Canvas::find_value_node(const String &id)
356 ValueNode::Handle::cast_const(
357 const_cast<const Canvas*>(this)->find_value_node(id)
361 ValueNode::ConstHandle
362 Canvas::find_value_node(const String &id)const
364 if(is_inline() && parent_)
365 return parent_->find_value_node(id);
368 throw Exception::IDNotFound("Empty ID");
370 // If we do not have any resolution, then we assume that the
371 // request is for this immediate canvas
372 if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
373 return value_node_list_.find(id);
375 String canvas_id(id,0,id.rfind(':'));
376 String value_node_id(id,id.rfind(':')+1);
377 if(canvas_id.empty())
379 //synfig::warning("constfind:value_node_id: "+value_node_id);
380 //synfig::warning("constfind:canvas_id: "+canvas_id);
382 return find_canvas(canvas_id)->value_node_list_.find(value_node_id);
386 Canvas::surefind_value_node(const String &id)
388 if(is_inline() && parent_)
389 return parent_->surefind_value_node(id);
392 throw Exception::IDNotFound("Empty ID");
394 // If we do not have any resolution, then we assume that the
395 // request is for this immediate canvas
396 if(id.find_first_of(':')==string::npos && id.find_first_of('#')==string::npos)
397 return value_node_list_.surefind(id);
399 String canvas_id(id,0,id.rfind(':'));
400 String value_node_id(id,id.rfind(':')+1);
401 if(canvas_id.empty())
404 return surefind_canvas(canvas_id)->value_node_list_.surefind(value_node_id);
408 Canvas::add_value_node(ValueNode::Handle x, const String &id)
410 if(is_inline() && parent_)
411 return parent_->add_value_node(x,id);
412 // throw runtime_error("You cannot add a ValueNode to an inline Canvas");
416 throw runtime_error("ValueNode is already exported");
419 throw Exception::BadLinkName("Empty ID");
421 if(id.find_first_of(':',0)!=string::npos)
422 throw Exception::BadLinkName("Bad character");
427 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
428 throw Exception::IDNotFound("add_value_node()");
431 throw Exception::IDAlreadyExists(id);
433 catch(Exception::IDNotFound)
438 x->set_parent_canvas(this);
440 if(!value_node_list_.add(x))
442 synfig::error("Unable to add ValueNode");
443 throw std::runtime_error("Unable to add ValueNode");
453 Canvas::rename_value_node(ValueNode::Handle x, const String &id)
456 throw Exception::BadLinkName("Empty ID");
458 if(id.find_first_of(": ",0)!=string::npos)
459 throw Exception::BadLinkName("Bad character");
463 if(PlaceholderValueNode::Handle::cast_dynamic(value_node_list_.find(id)))
464 throw Exception::IDNotFound("rename_value_node");
465 throw Exception::IDAlreadyExists(id);
467 catch(Exception::IDNotFound)
477 Canvas::remove_value_node(ValueNode::Handle x)
479 if(is_inline() && parent_)
480 return parent_->remove_value_node(x);
481 // throw Exception::IDNotFound("Canvas::remove_value_node() was called from an inline canvas");
484 throw Exception::IDNotFound("Canvas::remove_value_node() was passed empty handle");
486 if(!value_node_list_.erase(x))
487 throw Exception::IDNotFound("Canvas::remove_value_node(): ValueNode was not found inside of this canvas");
489 //x->set_parent_canvas(0);
496 Canvas::surefind_canvas(const String &id)
498 if(is_inline() && parent_)
499 return parent_->surefind_canvas(id);
504 // If the ID contains a "#" character, then a filename is
505 // expected on the left side.
506 if(id.find_first_of('#')!=string::npos)
508 // If '#' is the first character, remove it
509 // and attempt to parse the ID again.
511 return surefind_canvas(String(id,1));
513 //! \todo This needs alot more optimization
514 String file_name(id,0,id.find_first_of('#'));
515 String external_id(id,id.find_first_of('#')+1);
517 file_name=unix_to_local_path(file_name);
519 Canvas::Handle external_canvas;
521 // If the composition is already open, then use it.
522 if(externals_.count(file_name))
523 external_canvas=externals_[file_name];
526 if(is_absolute_path(file_name))
527 external_canvas=open_canvas(file_name);
529 external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPERATOR+file_name);
532 throw Exception::FileNotFound(file_name);
533 externals_[file_name]=external_canvas;
536 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id));
539 // If we do not have any resolution, then we assume that the
540 // request is for this immediate canvas
541 if(id.find_first_of(':')==string::npos)
543 Children::iterator iter;
545 // Search for the image in the image list,
546 // and return it if it is found
547 for(iter=children().begin();iter!=children().end();iter++)
548 if(id==(*iter)->get_id())
551 // Create a new canvas and return it
552 //synfig::warning("Implicitly creating canvas named "+id);
553 return new_child_canvas(id);
556 // If the first character is the seperator, then
557 // this references the root canvas.
559 return get_root()->surefind_canvas(string(id,1));
561 // Now we know that the requested Canvas is in a child
562 // of this canvas. We have to find that canvas and
563 // call "find_canvas" on it, and return the result.
565 String canvas_name=string(id,0,id.find_first_of(':'));
567 Canvas::Handle child_canvas=surefind_canvas(canvas_name);
569 return child_canvas->surefind_canvas(string(id,id.find_first_of(':')+1));
573 Canvas::find_canvas(const String &id)
576 Canvas::Handle::cast_const(
577 const_cast<const Canvas*>(this)->find_canvas(id)
582 Canvas::find_canvas(const String &id)const
584 if(is_inline() && parent_)return parent_->find_canvas(id);
589 // If the ID contains a "#" character, then a filename is
590 // expected on the left side.
591 if(id.find_first_of('#')!=string::npos)
593 // If '#' is the first character, remove it
594 // and attempt to parse the ID again.
596 return find_canvas(String(id,1));
598 //! \todo This needs alot more optimization
599 String file_name(id,0,id.find_first_of('#'));
600 String external_id(id,id.find_first_of('#')+1);
602 file_name=unix_to_local_path(file_name);
604 Canvas::Handle external_canvas;
606 // If the composition is already open, then use it.
607 if(externals_.count(file_name))
608 external_canvas=externals_[file_name];
611 if(is_absolute_path(file_name))
612 external_canvas=open_canvas(file_name);
614 external_canvas=open_canvas(get_file_path()+ETL_DIRECTORY_SEPERATOR+file_name);
617 throw Exception::FileNotFound(file_name);
618 externals_[file_name]=external_canvas;
621 return Handle::cast_const(external_canvas.constant()->find_canvas(external_id));
624 // If we do not have any resolution, then we assume that the
625 // request is for this immediate canvas
626 if(id.find_first_of(':')==string::npos)
628 Children::const_iterator iter;
630 // Search for the image in the image list,
631 // and return it if it is found
632 for(iter=children().begin();iter!=children().end();iter++)
633 if(id==(*iter)->get_id())
636 throw Exception::IDNotFound("Child Canvas in Parent Canvas: (child)"+id);
639 // If the first character is the seperator, then
640 // this references the root canvas.
641 if(id.find_first_of(':')==0)
642 return get_root()->find_canvas(string(id,1));
644 // Now we know that the requested Canvas is in a child
645 // of this canvas. We have to find that canvas and
646 // call "find_canvas" on it, and return the result.
648 String canvas_name=string(id,0,id.find_first_of(':'));
650 Canvas::ConstHandle child_canvas=find_canvas(canvas_name);
652 return child_canvas->find_canvas(string(id,id.find_first_of(':')+1));
659 return new Canvas("Untitled");
663 Canvas::push_back(etl::handle<Layer> x)
666 // int i(x->count());
668 //if(x->count()!=i+1)synfig::info("push_back before %d, after %d",i,x->count());
672 Canvas::push_front(etl::handle<Layer> x)
675 // int i(x->count());
677 //if(x->count()!=i+1)synfig::error("push_front before %d, after %d",i,x->count());
681 Canvas::insert(iterator iter,etl::handle<Layer> x)
683 // int i(x->count());
684 CanvasBase::insert(iter,x);
686 /*if(x->count()!=i+1)
688 synfig::error(__FILE__":%d: Canvas::insert(): ***FAILURE*** before %d, after %d",__LINE__,i,x->count());
690 //throw runtime_error("Canvas Insertion Failed");
699 LooseHandle correct_canvas(this);
700 //while(correct_canvas->is_inline())correct_canvas=correct_canvas->parent();
701 Layer::LooseHandle loose_layer(x);
703 x->signal_added_to_group().connect(
707 &Canvas::add_group_pair
712 x->signal_removed_from_group().connect(
716 &Canvas::remove_group_pair
723 if(!x->get_group().empty())
724 add_group_pair(x->get_group(),x);
731 Canvas::push_back_simple(etl::handle<Layer> x)
733 CanvasBase::insert(end(),x);
738 Canvas::erase(Canvas::iterator iter)
740 if(!(*iter)->get_group().empty())
741 remove_group_pair((*iter)->get_group(),(*iter));
743 // HACK: We really shouldn't be wiping
744 // out these signals entirely. We should
745 // only be removing the specific connections
746 // that we made. At the moment, I'm too
747 // lazy to add the code to keep track
748 // of those connections, and no one else
749 // is using these signals, so I'll just
750 // leave these next two lines like they
751 // are for now - darco 07-30-2004
752 (*iter)->signal_added_to_group().clear();
753 (*iter)->signal_removed_from_group().clear();
755 if(!op_flag_)remove_child(iter->get());
757 CanvasBase::erase(iter);
758 if(!op_flag_)changed();
762 Canvas::clone(const GUID& deriv_guid)const
769 name=get_id()+"_CLONE";
771 throw runtime_error("Cloning of non-inline canvases is not yet suported");
774 Handle canvas(new Canvas(name));
778 canvas->is_inline_=true;
780 //canvas->set_inline(parent());
783 canvas->set_guid(get_guid()^deriv_guid);
786 for(iter=begin();iter!=end();++iter)
788 Layer::Handle layer((*iter)->clone(deriv_guid));
791 assert(layer.count()==1);
793 canvas->push_back(layer);
794 if(!(layer.count()>1))
796 synfig::error("Canvas::clone(): Cloned layer insertion failure!");
797 synfig::error("Canvas::clone(): \tlayer.count()=%d",layer.count());
798 synfig::error("Canvas::clone(): \tlayer->get_name()=%s",layer->get_name().c_str());
799 synfig::error("Canvas::clone(): \tbefore size()=%d",presize);
800 synfig::error("Canvas::clone(): \tafter size()=%d",size());
802 assert(layer.count()>1);
806 synfig::error("Unable to clone layer");
810 canvas->signal_group_pair_removed().clear();
811 canvas->signal_group_pair_added().clear();
817 Canvas::set_inline(LooseHandle parent)
819 if(is_inline_ && parent_)
828 // Have the parent inherit all of the group stuff
830 std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
832 for(iter=group_db_.begin();iter!=group_db_.end();++iter)
834 parent->group_db_[iter->first].insert(iter->second.begin(),iter->second.end());
837 rend_desc()=parent->rend_desc();
841 Canvas::create_inline(Handle parent)
844 //if(parent->is_inline())
845 // return create_inline(parent->parent());
847 Handle canvas(new Canvas("inline"));
848 canvas->set_inline(parent);
853 Canvas::new_child_canvas()
855 if(is_inline() && parent_)
856 return parent_->new_child_canvas();
857 // runtime_error("You cannot create a child Canvas in an inline Canvas");
859 // Create a new canvas
860 children().push_back(create());
861 Canvas::Handle canvas(children().back());
863 canvas->parent_=this;
865 canvas->rend_desc()=rend_desc();
871 Canvas::new_child_canvas(const String &id)
873 if(is_inline() && parent_)
874 return parent_->new_child_canvas(id);
875 // runtime_error("You cannot create a child Canvas in an inline Canvas");
877 // Create a new canvas
878 children().push_back(create());
879 Canvas::Handle canvas(children().back());
882 canvas->parent_=this;
883 canvas->rend_desc()=rend_desc();
889 Canvas::add_child_canvas(Canvas::Handle child_canvas, const synfig::String& id)
891 if(is_inline() && parent_)
892 return parent_->add_child_canvas(child_canvas,id);
894 if(child_canvas->parent() && !child_canvas->is_inline())
895 throw std::runtime_error("Cannot add child canvas because it belongs to someone else!");
898 throw runtime_error("Invalid ID");
903 throw Exception::IDAlreadyExists(id);
905 catch(Exception::IDNotFound)
907 if(child_canvas->is_inline())
908 child_canvas->is_inline_=false;
909 child_canvas->id_=id;
910 children().push_back(child_canvas);
911 child_canvas->parent_=this;
918 Canvas::remove_child_canvas(Canvas::Handle child_canvas)
920 if(is_inline() && parent_)
921 return parent_->remove_child_canvas(child_canvas);
923 if(child_canvas->parent_!=this)
924 throw runtime_error("Given child does not belong to me");
926 if(find(children().begin(),children().end(),child_canvas)==children().end())
927 throw Exception::IDNotFound(child_canvas->get_id());
929 children().remove(child_canvas);
931 child_canvas->parent_=0;
935 Canvas::set_file_name(const String &file_name)
938 parent()->set_file_name(file_name);
941 file_name_=file_name;
942 signal_file_name_changed_();
947 Canvas::signal_file_name_changed()
950 return signal_file_name_changed();
952 return signal_file_name_changed_;
956 Canvas::get_file_name()const
959 return parent()->get_file_name();
964 Canvas::get_file_path()const
967 return parent()->get_file_path();
968 return dirname(file_name_);
973 Canvas::get_meta_data(const String& key)const
975 if(!meta_data_.count(key))
977 return meta_data_.find(key)->second;
981 Canvas::set_meta_data(const String& key, const String& data)
983 if(meta_data_[key]!=data)
985 meta_data_[key]=data;
986 signal_meta_data_changed()(key);
987 signal_meta_data_changed(key)();
992 Canvas::erase_meta_data(const String& key)
994 if(meta_data_.count(key))
996 meta_data_.erase(key);
997 signal_meta_data_changed()(key);
998 signal_meta_data_changed(key)();
1003 Canvas::get_meta_data_keys()const
1005 std::list<String> ret;
1007 std::map<String,String>::const_iterator iter;
1009 for(iter=meta_data_.begin();!(iter==meta_data_.end());++iter)
1010 ret.push_back(iter->first);
1016 synfig::optimize_layers(Context context, Canvas::Handle op_canvas)
1020 std::vector< std::pair<float,Layer::Handle> > sort_list;
1023 // Go ahead and start romping through the canvas to paste
1024 for(iter=context,i=0;*iter;iter++,i++)
1026 Layer::Handle layer=*iter;
1027 float z_depth(layer->get_z_depth()*1.0001+i);
1029 // If the layer isn't active, don't worry about it
1030 if(!layer->active())
1033 // Any layer with an amount of zero is implicitly disabled.
1034 ValueBase value(layer->get_param("amount"));
1035 if(value.get_type()==ValueBase::TYPE_REAL && value.get(Real())==0)
1038 Layer_PasteCanvas* paste_canvas(static_cast<Layer_PasteCanvas*>(layer.get()));
1039 if(layer->get_name()=="PasteCanvas" && paste_canvas->get_time_offset()==0)
1041 Canvas::Handle sub_canvas(Canvas::create_inline(op_canvas));
1042 optimize_layers(paste_canvas->get_sub_canvas()->get_context(),sub_canvas);
1043 //#define SYNFIG_OPTIMIZE_PASTE_CANVAS 1
1045 #ifdef SYNFIG_OPTIMIZE_PASTE_CANVAS
1046 Canvas::iterator sub_iter;
1047 // Determine if we can just remove the paste canvas
1049 if(paste_canvas->get_blend_method()==Color::BLEND_COMPOSITE && paste_canvas->get_amount()==1.0f && paste_canvas->get_zoom()==0 && paste_canvas->get_time_offset()==0 && paste_canvas->get_origin()==Point(0,0))
1051 for(sub_iter=sub_canvas->begin();sub_iter!=sub_canvas->end();++sub_iter)
1053 Layer* layer=sub_iter->get();
1055 // any layers that deform end up breaking things
1056 // so do things the old way if we run into anything like this
1057 if(!dynamic_cast<Layer_NoDeform*>(layer))
1060 ValueBase value(layer->get_param("blend_method"));
1061 if(value.get_type()!=ValueBase::TYPE_INTEGER || value.get(int())!=(int)Color::BLEND_COMPOSITE)
1065 // It has turned out that we don't need a paste canvas
1066 // layer, so just go ahead and add all the layers onto
1067 // the current stack and be done with it
1068 while(sub_canvas->size())
1070 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,sub_canvas->front()));
1071 //op_canvas->push_back_simple(sub_canvas->front());
1072 sub_canvas->pop_front();
1077 Layer::Handle new_layer(Layer::create("PasteCanvas"));
1078 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_do_not_muck_with_time(true);
1079 Layer::ParamList param_list(paste_canvas->get_param_list());
1080 //param_list.erase("canvas");
1081 new_layer->set_param_list(param_list);
1082 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_sub_canvas(sub_canvas);
1083 dynamic_cast<Layer_PasteCanvas*>(new_layer.get())->set_do_not_muck_with_time(false);
1087 sort_list.push_back(std::pair<float,Layer::Handle>(z_depth,layer));
1088 //op_canvas->push_back_simple(layer);
1092 stable_sort(sort_list.begin(),sort_list.end());
1093 std::vector< std::pair<float,Layer::Handle> >::iterator iter2;
1094 for(iter2=sort_list.begin();iter2!=sort_list.end();++iter2)
1095 op_canvas->push_back_simple(iter2->second);
1096 op_canvas->op_flag_=true;
1100 Canvas::get_times_vfunc(Node::time_set &set) const
1102 const_iterator i = begin(),
1105 for(; i != iend; ++i)
1107 const Node::time_set &tset = (*i)->get_times();
1108 set.insert(tset.begin(),tset.end());
1112 std::set<etl::handle<Layer> >
1113 Canvas::get_layers_in_group(const String&group)
1115 if(is_inline() && parent_)
1116 return parent_->get_layers_in_group(group);
1118 if(group_db_.count(group)==0)
1119 return std::set<etl::handle<Layer> >();
1120 return group_db_.find(group)->second;
1124 Canvas::get_groups()const
1126 if(is_inline() && parent_)
1127 return parent_->get_groups();
1129 std::set<String> ret;
1130 std::map<String,std::set<etl::handle<Layer> > >::const_iterator iter;
1131 for(iter=group_db_.begin();iter!=group_db_.end();++iter)
1132 ret.insert(iter->first);
1137 Canvas::get_group_count()const
1139 if(is_inline() && parent_)
1140 return parent_->get_group_count();
1142 return group_db_.size();
1146 Canvas::add_group_pair(String group, etl::handle<Layer> layer)
1148 group_db_[group].insert(layer);
1149 if(group_db_[group].size()==1)
1150 signal_group_added()(group);
1152 signal_group_changed()(group);
1154 signal_group_pair_added()(group,layer);
1156 if(is_inline() && parent_)
1157 return parent_->add_group_pair(group,layer);
1161 Canvas::remove_group_pair(String group, etl::handle<Layer> layer)
1163 group_db_[group].erase(layer);
1165 signal_group_pair_removed()(group,layer);
1167 if(group_db_[group].empty())
1169 group_db_.erase(group);
1170 signal_group_removed()(group);
1173 signal_group_changed()(group);
1175 if(is_inline() && parent_)
1176 return parent_->remove_group_pair(group,layer);
1180 Canvas::rename_group(const String&old_name,const String&new_name)
1182 if(is_inline() && parent_)
1183 return parent_->rename_group(old_name,new_name);
1186 std::map<String,std::set<etl::handle<Layer> > >::iterator iter;
1187 iter=group_db_.find(old_name);
1188 if(iter!=group_db_.end())
1189 for(++iter;iter!=group_db_.end() && iter->first.find(old_name)==0;iter=group_db_.find(old_name),++iter)
1191 String name(iter->first,old_name.size(),String::npos);
1193 rename_group(iter->first,name);
1197 std::set<etl::handle<Layer> > layers(get_layers_in_group(old_name));
1198 std::set<etl::handle<Layer> >::iterator iter;
1200 for(iter=layers.begin();iter!=layers.end();++iter)
1202 (*iter)->remove_from_group(old_name);
1203 (*iter)->add_to_group(new_name);