/* === S Y N F I G ========================================================= */ /*! \file canvastreestore.cpp ** \brief Template File ** ** $Id$ ** ** \legal ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley ** Copyright (c) 2008 Chris Moore ** Copyright (c) 2011 Carlos López ** ** This package is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License, or (at your option) any later version. ** ** This package is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** General Public License for more details. ** \endlegal */ /* ========================================================================= */ /* === H E A D E R S ======================================================= */ #ifdef USING_PCH # include "pch.h" #else #ifdef HAVE_CONFIG_H # include #endif #include "trees/canvastreestore.h" #include #include "iconcontroller.h" #include #include #include #include #include #include #include "cellrenderer/cellrenderer_value.h" #include "cellrenderer/cellrenderer_timetrack.h" #include #include #include "general.h" #endif /* === U S I N G =========================================================== */ using namespace std; using namespace etl; using namespace synfig; using namespace studio; /* === M A C R O S ========================================================= */ /* === G L O B A L S ======================================================= */ /* === P R O C E D U R E S ================================================= */ /* === M E T H O D S ======================================================= */ static CanvasTreeStore::Model& ModelHack() { static CanvasTreeStore::Model* model(0); if(!model)model=new CanvasTreeStore::Model; return *model; } CanvasTreeStore::CanvasTreeStore(etl::loose_handle canvas_interface_): Gtk::TreeStore(ModelHack()), canvas_interface_ (canvas_interface_) { } CanvasTreeStore::~CanvasTreeStore() { } ValueNode::Handle CanvasTreeStore::expandable_bone_parent(ValueNode::Handle node) { if ((!getenv("SYNFIG_DISABLE_EXPANDABLE_BONE_PARENTS")) && node->get_type() == type_bone_valuenode && (node->get_name() == "constant" || node->get_name() == "animated")) if (ValueNode::Handle bone_node = (*node)(canvas_interface()->get_time()).get(ValueNode_Bone::Handle())) return bone_node; return node; } void CanvasTreeStore::get_value_vfunc(const Gtk::TreeModel::iterator& iter, int column, Glib::ValueBase& value)const { if(column==model.value.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); Glib::Value x; g_value_init(x.gobj(),x.value_type()); if(!value_desc) { x.set(ValueBase()); } else if(value_desc.is_const()) x.set(value_desc.get_value()); else if(value_desc.is_value_node()) { Type &type(value_desc.get_value_type()); if (type == type_bone_object) { Time time(canvas_interface()->get_time()); Bone bone((*(value_desc.get_value_node()))(time).get(Bone())); String display(String(bone.get_name())); ValueNode_Bone::ConstHandle parent(bone.get_parent()); if (!parent->is_root()) display += " --> " + String((*parent->get_link("name"))(time).get(String())); x.set(display); } else if (type == type_bone_weight_pair) { Time time(canvas_interface()->get_time()); BoneWeightPair bone_weight_pair((*(value_desc.get_value_node()))(time).get(BoneWeightPair())); x.set(bone_weight_pair.get_string()); } else if (type == type_segment || type == type_list || type == type_bline_point) { x.set(value_desc.get_value_type().description.local_name); } else { x.set((*value_desc.get_value_node())(canvas_interface()->get_time())); } } else { synfig::error(__FILE__":%d: Unable to figure out value",__LINE__); return; } g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.is_value_node.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); Glib::Value x; g_value_init(x.gobj(),x.value_type()); x.set(value_desc && value_desc.is_value_node()); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.is_shared.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); Glib::Value x; g_value_init(x.gobj(),x.value_type()); x.set(value_desc.is_value_node() && value_desc.get_value_node()->rcount()>1); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.is_exported.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); Glib::Value x; g_value_init(x.gobj(),x.value_type()); x.set(value_desc.is_value_node() && value_desc.get_value_node()->is_exported()); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.is_canvas.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); Glib::Value x; g_value_init(x.gobj(),x.value_type()); x.set(!value_desc && (Canvas::Handle)(*iter)[model.canvas]); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.id.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); Glib::Value x; g_value_init(x.gobj(),x.value_type()); if(value_desc && value_desc.is_value_node()) x.set(value_desc.get_value_node()->get_id()); else if(!value_desc && Canvas::Handle((*iter)[model.canvas])) x.set(Canvas::Handle((*iter)[model.canvas])->get_id()); else return Gtk::TreeStore::get_value_vfunc(iter,column,value); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.is_editable.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); Glib::Value x; g_value_init(x.gobj(),x.value_type()); x.set(!value_desc.is_value_node() || synfigapp::is_editable(value_desc.get_value_node())); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.type.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); String stype, lname; Glib::Value x; g_value_init(x.gobj(),x.value_type()); // Set the type if(!value_desc) { if((*iter)[model.is_canvas]) x.set(_("Canvas")); } else { stype=value_desc.get_value_type().description.local_name; if(!value_desc.is_const()) stype+=" (" + value_desc.get_value_node()->get_local_name() + ")"; } x.set(stype.c_str()); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.label.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); Glib::Value x; g_value_init(x.gobj(),x.value_type()); // Set the type if(!value_desc) { Canvas::Handle canvas((*iter)[model.canvas]); if(canvas) { if(!canvas->get_id().empty()) x.set(canvas->get_id()); else if(!canvas->get_name().empty()) x.set(canvas->get_name()); else x.set(_("[Unnamed]")); // todo: what are the previous 6 lines for if we're going to overwrite it here? x.set(_("Canvas")); } return Gtk::TreeStore::get_value_vfunc(iter,column,value); } else { ValueNode::Handle value_node=value_desc.get_value_node(); // Setup the row's label if(value_node->get_id().empty()) x.set(Glib::ustring((*iter)[model.name])); else if(Glib::ustring((*iter)[model.name]).empty()) x.set(value_node->get_id()); else x.set(Glib::ustring((*iter)[model.name])+" ("+value_node->get_id()+')'); } g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.icon.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); if(!value_desc) return Gtk::TreeStore::get_value_vfunc(iter,column,value); Glib::Value > x; g_value_init(x.gobj(),x.value_type()); x.set(get_tree_pixbuf(value_desc.get_value_type())); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.interpolation_icon.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); if(!value_desc) return Gtk::TreeStore::get_value_vfunc(iter,column,value); Glib::Value > x; g_value_init(x.gobj(),x.value_type()); x.set(get_interpolation_pixbuf(value_desc.get_interpolation())); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.is_static.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); Glib::Value x; g_value_init(x.gobj(),x.value_type()); x.set(value_desc.get_static()); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else if(column==model.interpolation_icon_visible.index()) { synfigapp::ValueDesc value_desc((*iter)[model.value_desc]); Glib::Value x; g_value_init(x.gobj(),x.value_type()); bool is_visible((!value_desc.get_static()) && (value_desc.get_interpolation()!=INTERPOLATION_UNDEFINED) && (value_desc.get_interpolation()!=INTERPOLATION_MANUAL) && (value_desc.get_interpolation()!=INTERPOLATION_NIL)); x.set(is_visible); g_value_init(value.gobj(),x.value_type()); g_value_copy(x.gobj(),value.gobj()); } else Gtk::TreeStore::get_value_vfunc(iter,column,value); } bool CanvasTreeStore::find_first_value_desc(const synfigapp::ValueDesc& value_desc, Gtk::TreeIter& iter) { iter=children().begin(); while(iter && value_desc!=(*iter)[model.value_desc]) { if(!iter->children().empty()) { Gtk::TreeIter iter2(iter->children().begin()); //! \todo confirm that the && should be done before the || if((iter2 && value_desc==(*iter2)[model.value_desc]) || find_next_value_desc(value_desc, iter2)) { iter=iter2; return true; } } Gtk::TreeIter iter2(++iter); if(!iter2) iter=iter->parent(); else iter=iter2; } return (bool)iter && value_desc==(*iter)[model.value_desc]; } bool CanvasTreeStore::find_next_value_desc(const synfigapp::ValueDesc& value_desc, Gtk::TreeIter& iter) { if(!iter) return find_first_value_desc(value_desc,iter); if(iter) do { if(!iter->children().empty()) { Gtk::TreeIter iter2(iter->children().begin()); //! \todo confirm that the && should be done before the || if((iter2 && value_desc==(*iter2)[model.value_desc]) || find_next_value_desc(value_desc, iter2)) { iter=iter2; return true; } } Gtk::TreeIter iter2(++iter); if(!iter2) { iter=iter->parent(); if(iter)++iter; } else iter=iter2; } while(iter && value_desc!=(*iter)[model.value_desc]); return (bool)iter && value_desc==(*iter)[model.value_desc]; } bool CanvasTreeStore::find_first_value_node(const synfig::ValueNode::Handle& value_node, Gtk::TreeIter& iter) { // maybe replace the ValueNode_Const or ValueNode_Animated with the contained ValueNode_Bone // todo: do we need to do this in find_next_value_node, and find_*_value_desc too? synfig::ValueNode::Handle node(expandable_bone_parent(value_node)); iter=children().begin(); while(iter && node!=(ValueNode::Handle)(*iter)[model.value_node]) { if(!iter->children().empty()) { Gtk::TreeIter iter2(iter->children().begin()); //! \todo confirm that the && should be done before the || if((iter2 && node==(ValueNode::Handle)(*iter2)[model.value_node]) || find_next_value_node(node, iter2)) { iter=iter2; return true; } } Gtk::TreeIter iter2(++iter); if(!iter2) iter=iter->parent(); else iter=iter2; } return (bool)iter && node==(ValueNode::Handle)(*iter)[model.value_node]; } bool CanvasTreeStore::find_next_value_node(const synfig::ValueNode::Handle& value_node, Gtk::TreeIter& iter) { if(!iter) return find_first_value_node(value_node,iter); if(iter) do { if(!iter->children().empty()) { Gtk::TreeIter iter2(iter->children().begin()); //! \todo confirm that the && should be done before the || if((iter2 && value_node==(ValueNode::Handle)(*iter2)[model.value_node]) || find_next_value_node(value_node, iter2)) { iter=iter2; return true; } } Gtk::TreeIter iter2(++iter); if(!iter2) { iter=iter->parent(); if(iter)++iter; } else iter=iter2; } while(iter && value_node!=(ValueNode::Handle)(*iter)[model.value_node]); return (bool)iter && value_node==(ValueNode::Handle)(*iter)[model.value_node]; } void CanvasTreeStore::set_row(Gtk::TreeRow row,synfigapp::ValueDesc value_desc, bool do_children) { Gtk::TreeModel::Children children = row.children(); while(!children.empty() && erase(children.begin())) ; row[model.value_desc]=value_desc; try { //row[model.icon] = get_tree_pixbuf(value_desc.get_value_type()); if(value_desc.is_value_node()) { ValueNode::Handle value_node=value_desc.get_value_node(); // todo: if the parent is animated and expanded, and we drag the time slider so that it changes, // it's not updated. it still shows the previous bone valuenode. // maybe replace the ValueNode_Const or ValueNode_Animated with the contained ValueNode_Bone value_node = expandable_bone_parent(value_node); assert(value_node); row[model.value_node] = value_node; //row[model.is_canvas] = false; //row[model.is_value_node] = true; //row[model.is_editable] = synfigapp::is_editable(value_node); //row[model.id]=value_node->get_id(); // Set the canvas if(value_desc.parent_is_canvas()) row[model.canvas]=value_desc.get_canvas(); else row[model.canvas]=canvas_interface()->get_canvas(); LinkableValueNode::Handle linkable; // printf("%s:%d value_node = %s\n", __FILE__, __LINE__, value_node->get_description().c_str()); linkable=LinkableValueNode::Handle::cast_dynamic(value_node); // printf("linkable: %d; do_children: %d\n", bool(linkable), bool(do_children)); if(linkable && do_children) { row[model.link_count] = linkable->link_count(); LinkableValueNode::Vocab vocab(linkable->get_children_vocab()); LinkableValueNode::Vocab::iterator iter(vocab.begin()); for(int i=0;ilink_count();i++, iter++) { if(iter->get_hidden()) continue; Gtk::TreeRow child_row=*(append(row.children())); child_row[model.link_id] = i; child_row[model.canvas] = static_cast(row[model.canvas]); child_row[model.name] = linkable->link_local_name(i); child_row[model.tooltip] = iter->get_description(); child_row[model.child_param_desc] = *iter; set_row(child_row,synfigapp::ValueDesc(linkable,i)); } } return; } else { //row[model.is_value_node] = false; //row[model.is_editable] = true; //row[model.label] = Glib::ustring(row[model.name]); return; } } catch(synfig::Exception::IDNotFound x) { synfig::error(__FILE__":%d: IDNotFound thrown",__LINE__); erase(row); return; } // We should never get to this point assert(0); } void CanvasTreeStore::refresh_row(Gtk::TreeModel::Row &row, bool do_children) { synfigapp::ValueDesc value_desc=row[model.value_desc]; if(value_desc) { if((bool)row[model.is_value_node] != value_desc.is_value_node() || (!bool(row[model.is_value_node]) && row[model.link_count]!=0)) { set_row(row,value_desc,do_children); return; } if(row[model.is_value_node]) { ValueNode::Handle value_node(value_desc.get_value_node()); if(ValueNode::Handle(row[model.value_node])!=value_node) { rebuild_row(row,do_children); return; } //row[model.id]=value_node->get_id(); // Setup the row's label /* if(value_node->get_id().empty()) row[model.label] = Glib::ustring(row[model.name]); else if(Glib::ustring(row[model.name]).empty()) row[model.label] = value_node->get_id(); else row[model.label] = Glib::ustring(row[model.name])+" ("+value_node->get_id()+')'; */ LinkableValueNode::Handle linkable; linkable=LinkableValueNode::Handle::cast_dynamic(value_node); if(do_children && linkable && ((int)row[model.link_count] != linkable->link_count())) { // Gtk::TreeModel::Children children = row.children(); // while(!children.empty() && erase(children.begin())); set_row(row,value_desc); return; } } else { //row[model.label] = Glib::ustring(row[model.name]); //row[model.is_value_node] = false; //row[model.is_editable] = true; } } if(!do_children) return; Gtk::TreeModel::Children children = row.children(); Gtk::TreeModel::Children::iterator iter; if(!children.empty()) for(iter = children.begin(); iter != children.end(); ++iter) { Gtk::TreeRow row=*iter; refresh_row(row); } } void CanvasTreeStore::rebuild_row(Gtk::TreeModel::Row &row, bool do_children) { synfigapp::ValueDesc value_desc=(synfigapp::ValueDesc)row[model.value_desc]; if(value_desc && value_desc.get_value_node()) { ValueNode::Handle value_node; value_node=value_desc.get_value_node(); assert(value_node);if(!value_node)return; if(value_node && value_node!=(ValueNode::Handle)row[model.value_node]) { // Gtk::TreeModel::Children children = row.children(); // while(!children.empty() && erase(children.begin())); set_row(row,value_desc,do_children); return; } LinkableValueNode::Handle linkable; linkable=LinkableValueNode::Handle::cast_dynamic(value_node); if( do_children && linkable && (int)row[model.link_count] != linkable->link_count()) { // Gtk::TreeModel::Children children = row.children(); // while(!children.empty() && erase(children.begin())); set_row(row,value_desc); return; } //if(!value_node) // value_node=row[model.value_node]; row[model.id]=value_node->get_id(); // Setup the row's label if(value_node->get_id().empty()) row[model.label] = Glib::ustring(row[model.name]); else if(Glib::ustring(row[model.name]).empty()) row[model.label] = value_node->get_id(); else row[model.label] = Glib::ustring(row[model.name])+" ("+value_node->get_id()+')'; } else { row[model.label] = Glib::ustring(row[model.name]); row[model.is_value_node] = false; row[model.is_editable] = true; Gtk::TreeModel::Children children = row.children(); while(!children.empty() && erase(children.begin())) ; } if(!do_children) return; Gtk::TreeModel::Children children = row.children(); Gtk::TreeModel::Children::iterator iter; if(!children.empty()) for(iter = children.begin(); iter != children.end(); ++iter) { Gtk::TreeRow row=*iter; rebuild_row(row); } } CellRenderer_ValueBase* CanvasTreeStore::add_cell_renderer_value(Gtk::TreeView::Column* column) { const CanvasTreeStore::Model model; CellRenderer_ValueBase* ret; ret=Gtk::manage( new CellRenderer_ValueBase() ); column->pack_start(*ret,true); column->add_attribute(ret->property_value(), model.value); column->add_attribute(ret->property_editable(), model.is_editable); column->add_attribute(ret->property_canvas(), model.canvas); return ret; } CellRenderer_TimeTrack* CanvasTreeStore::add_cell_renderer_value_node(Gtk::TreeView::Column* column) { const CanvasTreeStore::Model model; CellRenderer_TimeTrack* ret; ret = Gtk::manage( new CellRenderer_TimeTrack() ); column->pack_start(*ret,true); //column->add_attribute(ret->property_visible(), model.is_value_node); column->add_attribute(ret->property_value_desc(), model.value_desc); column->add_attribute(ret->property_canvas(), model.canvas); return ret; }