/* === S Y N F I G ========================================================= */
/*! \file keyframetreestore.cpp
** \brief Template File
**
** $Id$
**
** \legal
** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
** Copyright (c) 2007, 2008 Chris Moore
** Copyright (c) 2012-2013 Konstantin Dmitriev
**
** 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 <config.h>
#endif
#include <cstdlib>
#include <ctime>
#include <gtkmm/button.h>
#include <gtkmm/treerowreference.h>
#include <synfig/general.h>
#include <synfig/canvas.h>
#include <synfig/keyframe.h>
#include <synfig/exception.h>
#include <synfig/valuenode.h>
#include <synfig/valuenodes/valuenode_timedswap.h>
#include <synfigapp/action.h>
#include <synfigapp/instance.h>
#include <gui/iconcontroller.h>
#include <gui/onemoment.h>
#include "keyframetreestore.h"
#include <gui/localization.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 ======================================================= */
// KeyframeTreeStore_Class KeyframeTreeStore::keyframe_tree_store_class_;
/* === C L A S S E S & S T R U C T S ======================================= */
struct _keyframe_iterator
{
synfig::KeyframeList::iterator iter;
int ref_count;
int index;
};
/*
Gtk::TreeModel::iterator keyframe_iter_2_model_iter(synfig::KeyframeList::iterator iter,int index)
{
Gtk::TreeModel::iterator ret;
_keyframe_iterator*& data(static_cast<_keyframe_iterator*&>(ret->gobj()->user_data));
data=new _keyframe_iterator();
data->ref_count=1;
data->iter=iter;
data->index=index;
return ret;
}
*/
synfig::KeyframeList::iterator model_iter_2_keyframe_iter(Gtk::TreeModel::iterator iter)
{
_keyframe_iterator* data(static_cast<_keyframe_iterator*>(iter->gobj()->user_data));
if(!data)
throw std::runtime_error("bad data");
return data->iter;
}
int get_index_from_model_iter(Gtk::TreeModel::iterator iter)
{
_keyframe_iterator* data(static_cast<_keyframe_iterator*>(iter->gobj()->user_data));
if(!data)
throw std::runtime_error("bad data");
return data->index;
}
/*
#ifndef TreeRowReferenceHack
class TreeRowReferenceHack
{
GtkTreeRowReference *gobject_;
public:
TreeRowReferenceHack():
gobject_(0)
{
}
TreeRowReferenceHack(const Glib::RefPtr<Gtk::TreeModel>& model, const Gtk::TreeModel::Path& path):
gobject_ ( gtk_tree_row_reference_new(model->gobj(), const_cast<GtkTreePath*>(path.gobj())) )
{
}
TreeRowReferenceHack(const TreeRowReferenceHack &x):
gobject_ ( x.gobject_?gtk_tree_row_reference_copy(x.gobject_):0 )
{
}
void swap(TreeRowReferenceHack & other)
{
GtkTreeRowReference *const temp = gobject_;
gobject_ = other.gobject_;
other.gobject_ = temp;
}
const TreeRowReferenceHack &
operator=(const TreeRowReferenceHack &rhs)
{
TreeRowReferenceHack temp (rhs);
swap(temp);
return *this;
}
~TreeRowReferenceHack()
{
if(gobject_)
gtk_tree_row_reference_free(gobject_);
}
Gtk::TreeModel::Path get_path() { return Gtk::TreeModel::Path(gtk_tree_row_reference_get_path(gobject_),false); }
GtkTreeRowReference *gobj() { return gobject_; }
};
#endif
*/
/* === P R O C E D U R E S ================================================= */
void clear_iterator(GtkTreeIter* iter)
{
iter->stamp=0;
iter->user_data=iter->user_data2=iter->user_data3=0;
}
/* === M E T H O D S ======================================================= */
KeyframeTreeStore::KeyframeTreeStore(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface_):
Glib::ObjectBase ("KeyframeTreeStore"),
//! \todo what is going on here? why the need for this KeyframeTreeStore_Class at all?
// Glib::Object (Glib::ConstructParams(keyframe_tree_store_class_.init(), (char*) 0, (char*) 0)),
canvas_interface_ (canvas_interface_)
{
reset_stamp();
//reset_path_table();
//connect some events
canvas_interface()->signal_keyframe_added().connect(sigc::mem_fun(*this,&studio::KeyframeTreeStore::add_keyframe));
canvas_interface()->signal_keyframe_removed().connect(sigc::mem_fun(*this,&studio::KeyframeTreeStore::remove_keyframe));
canvas_interface()->signal_keyframe_changed().connect(sigc::mem_fun(*this,&studio::KeyframeTreeStore::change_keyframe));
//canvas_interface()->signal_keyframe_selected().connect(sigc::mem_fun(*this,&studio::KeyframeTreeStore::select_keyframe));
}
KeyframeTreeStore::~KeyframeTreeStore()
{
if (getenv("SYNFIG_DEBUG_DESTRUCTORS"))
synfig::info("KeyframeTreeStore::~KeyframeTreeStore(): Deleted");
}
Glib::RefPtr<KeyframeTreeStore>
KeyframeTreeStore::create(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface_)
{
KeyframeTreeStore *store(new KeyframeTreeStore(canvas_interface_));
Glib::RefPtr<KeyframeTreeStore> ret(store);
assert(ret);
return ret;
}
void
KeyframeTreeStore::reset_stamp()
{
stamp_=time(0)+reinterpret_cast<intptr_t>(this);
}
/*
void
KeyframeTreeStore::reset_path_table()
{
Gtk::TreeModel::Children::iterator iter;
const Gtk::TreeModel::Children children(children());
path_table_.clear();
for(iter = children.begin(); iter != children.end(); ++iter)
{
Gtk::TreeModel::Row row(*iter);
path_table_[(Keyframe)row[model.keyframe]]=TreeRowReferenceHack(Glib::RefPtr<KeyframeTreeStore>(this),Gtk::TreePath(row));
}
}
*/
inline bool
KeyframeTreeStore::iterator_sane(const GtkTreeIter* iter)const
{
if(iter && iter->stamp==stamp_)
return true;
g_warning("KeyframeTreeStore::iterator_sane(): Bad iterator stamp");
return false;
}
inline bool
KeyframeTreeStore::iterator_sane(const Gtk::TreeModel::iterator& iter)const
{
return iterator_sane(iter->gobj());
}
inline void
KeyframeTreeStore::dump_iterator(const GtkTreeIter* /*gtk_iter*/, const Glib::ustring &/*name*/)const
{
#if 0
if(!gtk_iter)
{
g_warning("KeyframeTreeStore::dump_iterator: \"%s\" is NULL (Root?)",name.c_str());
return;
}
_keyframe_iterator *iter(static_cast<_keyframe_iterator*>(gtk_iter->user_data));
if(gtk_iter->stamp!=stamp_ || !iter)
{
g_warning("KeyframeTreeStore::dump_iterator: \"%s\" is INVALID",name.c_str());
return;
}
if((unsigned)iter->index>=canvas_interface()->get_canvas()->keyframe_list().size())
g_warning("KeyframeTreeStore::dump_iterator: \"%s\"(%p) has bad index(index:%d)",name.c_str(),gtk_iter,iter->index);
g_warning("KeyframeTreeStore::dump_iterator: \"%s\"(%p) ref:%d, index:%d, time:%s",name.c_str(),gtk_iter,iter->ref_count,iter->index,iter->iter->get_time().get_string().c_str());
#endif
}
inline void
KeyframeTreeStore::dump_iterator(const Gtk::TreeModel::iterator& iter, const Glib::ustring &name)const
{
dump_iterator(iter->gobj(),name);
}
int
KeyframeTreeStore::time_sorter(const Gtk::TreeModel::iterator &rhs,const Gtk::TreeModel::iterator &lhs)
{
const Model model;
_keyframe_iterator *rhs_iter(static_cast<_keyframe_iterator*>(rhs->gobj()->user_data));
_keyframe_iterator *lhs_iter(static_cast<_keyframe_iterator*>(lhs->gobj()->user_data));
Time diff(rhs_iter->iter->get_time()-lhs_iter->iter->get_time());
if(diff<0)
return -1;
if(diff>0)
return 1;
return 0;
}
int
KeyframeTreeStore::description_sorter(const Gtk::TreeModel::iterator &rhs,const Gtk::TreeModel::iterator &lhs)
{
const Model model;
_keyframe_iterator *rhs_iter(static_cast<_keyframe_iterator*>(rhs->gobj()->user_data));
_keyframe_iterator *lhs_iter(static_cast<_keyframe_iterator*>(lhs->gobj()->user_data));
int comp = rhs_iter->iter->get_description().compare(lhs_iter->iter->get_description());
if (comp > 0) return 1;
if (comp < 0) return -1;
return 0;
}
void
KeyframeTreeStore::set_value_impl(const Gtk::TreeModel::iterator& row, int column, const Glib::ValueBase& value)
{
if(!iterator_sane(row))
return;
if(column>=get_n_columns_vfunc())
{
g_warning("KeyframeTreeStore::set_value_impl: Bad column (%d)",column);
return;
}
if(!g_value_type_compatible(G_VALUE_TYPE(value.gobj()),get_column_type_vfunc(column)))
{
g_warning("KeyframeTreeStore::set_value_impl: Bad value type");
return;
}
_keyframe_iterator *iter(static_cast<_keyframe_iterator*>(row.gobj()->user_data));
try
{
if(column==model.time_delta.index())
{
Glib::Value<synfig::Time> x;
g_value_init(x.gobj(),model.time.type());
g_value_copy(value.gobj(),x.gobj());
Time new_delta(x.get());
if(new_delta<=Time::zero()+Time::epsilon())
{
// Bad value
return;
}
Time old_delta((*row)[model.time_delta]);
if(old_delta<=Time::zero()+Time::epsilon())
{
// Bad old delta
return;
}
// row(row) on the next line is bad - don't use it, because it leaves 'row' uninitialized
//Gtk::TreeModel::iterator row(row);
//row++;
//if(!row)return;
Time change_delta(new_delta-old_delta);
if(change_delta<=Time::zero()+Time::epsilon() &&change_delta>=Time::zero()-Time::epsilon())
{
// Not an error, just no change
return;
}
{
Keyframe keyframe((*row)[model.keyframe]);
synfigapp::Action::Handle action(synfigapp::Action::create("KeyframeSetDelta"));
if(!action)return;
action->set_param("canvas",canvas_interface()->get_canvas());
action->set_param("canvas_interface",canvas_interface());
action->set_param("keyframe",keyframe);
action->set_param("delta",change_delta);
canvas_interface()->get_instance()->perform_action(action);
}
return;
}
else
if(column==model.time.index())
{
OneMoment one_moment;
Glib::Value<synfig::Time> x;
g_value_init(x.gobj(),model.time.type());
g_value_copy(value.gobj(),x.gobj());
synfig::Keyframe keyframe(*iter->iter);
synfig::info("KeyframeTreeStore::set_value_impl():old_time=%s",keyframe.get_time().get_string().c_str());
keyframe.set_time(x.get());
synfig::info("KeyframeTreeStore::set_value_impl():new_time=%s",keyframe.get_time().get_string().c_str());
synfigapp::Action::Handle action(synfigapp::Action::create("KeyframeSet"));
if(!action)
return;
action->set_param("canvas",canvas_interface()->get_canvas());
action->set_param("canvas_interface",canvas_interface());
action->set_param("keyframe",keyframe);
canvas_interface()->get_instance()->perform_action(action);
}
else if(column==model.description.index())
{
Glib::Value<Glib::ustring> x;
g_value_init(x.gobj(),model.description.type());
g_value_copy(value.gobj(),x.gobj());
synfig::Keyframe keyframe(*iter->iter);
keyframe.set_description(x.get());
synfigapp::Action::Handle action(synfigapp::Action::create("KeyframeSet"));
if(!action)
return;
action->set_param("canvas",canvas_interface()->get_canvas());
action->set_param("canvas_interface",canvas_interface());
action->set_param("keyframe",keyframe);
canvas_interface()->get_instance()->perform_action(action);
}
else if(column==model.active.index())
{
synfig::Keyframe keyframe((*row)[model.keyframe]);
Glib::Value<bool> x;
g_value_init(x.gobj(),model.active.type());
g_value_copy(value.gobj(),x.gobj());
synfigapp::Action::Handle action(synfigapp::Action::create("KeyframeToggl"));
if(!action)
return;
action->set_param("canvas",canvas_interface()->get_canvas());
action->set_param("canvas_interface",canvas_interface());
action->set_param("keyframe",keyframe);
action->set_param("new_status",bool(x.get()));
canvas_interface()->get_instance()->perform_action(action);
}
else if(column==model.keyframe.index())
{
g_warning("KeyframeTreeStore::set_value_impl: This column is read-only");
}
else
{
assert(0);
}
}
catch(std::exception& x)
{
g_warning("%s", x.what());
}
}
Gtk::TreeModelFlags
KeyframeTreeStore::get_flags_vfunc ()
{
return Gtk::TREE_MODEL_LIST_ONLY;
}
int
KeyframeTreeStore::get_n_columns_vfunc ()
{
return model.size();
}
GType
KeyframeTreeStore::get_column_type_vfunc (int index)
{
return model.types()[index];
}
bool
KeyframeTreeStore::iter_next_vfunc (const iterator& xiter, iterator& iter_next) const
{
if(!iterator_sane(xiter)) return false;
_keyframe_iterator *iter(static_cast<_keyframe_iterator*>(xiter.gobj()->user_data));
if(iter->iter==canvas_interface()->get_canvas()->keyframe_list().end())
return false;
_keyframe_iterator *next(new _keyframe_iterator());
iter_next.gobj()->user_data=static_cast<gpointer>(next);
next->ref_count=1;
next->index=iter->index+1;
next->iter=iter->iter;
++next->iter;
if(next->iter==canvas_interface()->get_canvas()->keyframe_list().end())
return false;
iter_next.gobj()->stamp=stamp_;
return true;
}
/*
bool
KeyframeTreeStore::iter_next_vfunc (GtkTreeIter* gtk_iter)
{
if(!iterator_sane(gtk_iter)) return false;
_keyframe_iterator *iter(static_cast<_keyframe_iterator*>(gtk_iter->user_data));
// If we are already at the end, then we are very invalid
if(iter->iter==canvas_interface()->get_canvas()->keyframe_list().end())
return false;
++(iter->iter);
if(iter->iter==canvas_interface()->get_canvas()->keyframe_list().end())
{
--(iter->iter);
return false;
}
(iter->index)++;
return true;
}
bool
KeyframeTreeStore::iter_children_vfunc (GtkTreeIter* gtk_iter, const GtkTreeIter* parent)
{
dump_iterator(gtk_iter,"gtk_iter");
dump_iterator(parent,"parent");
if(!parent || !iterator_sane(parent))
{
clear_iterator(gtk_iter);
return false;
}
_keyframe_iterator *iter(new _keyframe_iterator());
iter->ref_count=1;
iter->index=0;
iter->iter=canvas_interface()->get_canvas()->keyframe_list().begin();
gtk_iter->user_data=static_cast<gpointer>(iter);
gtk_iter->stamp=stamp_;
return true;
}
bool
KeyframeTreeStore::iter_has_child_vfunc (const GtkTreeIter*parent)
{
dump_iterator(parent,"parent");
if(parent)
return false;
return true;
}
int
KeyframeTreeStore::iter_n_children_vfunc (const GtkTreeIter* parent)
{
dump_iterator(parent,"parent");
if(parent)
return 0;
return canvas_interface()->get_canvas()->keyframe_list().size();
}
*/
int
KeyframeTreeStore::iter_n_root_children_vfunc () const
{
return canvas_interface()->get_canvas()->keyframe_list().size();
}
bool
KeyframeTreeStore::iter_nth_root_child_vfunc (int n, iterator& xiter)const
{
if(canvas_interface()->get_canvas()->keyframe_list().size()==0)
{
return false;
}
if(n<0)
{
g_warning("KeyframeTreeStore::iter_nth_root_child_vfunc: Out of range (negative index)");
return false;
}
if(n && (unsigned)n>=canvas_interface()->get_canvas()->keyframe_list().size())
{
g_warning("KeyframeTreeStore::iter_nth_child_vfunc: Out of range (large index)");
return false;
}
_keyframe_iterator *iter(new _keyframe_iterator());
iter->ref_count=1;
iter->index=n;
iter->iter=canvas_interface()->get_canvas()->keyframe_list().begin();
while(n--)
{
if(iter->iter==canvas_interface()->get_canvas()->keyframe_list().end())
{
g_warning("KeyframeTreeStore::iter_nth_child_vfunc: >>>BUG<<< in %s on line %d",__FILE__,__LINE__);
delete iter;
return false;
}
++iter->iter;
}
xiter.gobj()->user_data=static_cast<gpointer>(iter);
xiter.gobj()->stamp=stamp_;
return true;
}
/*
bool
KeyframeTreeStore::iter_nth_child_vfunc (GtkTreeIter* gtk_iter, const GtkTreeIter* parent, int n)
{
dump_iterator(parent,"parent");
if(parent)
{
g_warning("KeyframeTreeStore::iter_nth_child_vfunc: I am a list");
clear_iterator(gtk_iter);
return false;
}
_keyframe_iterator *iter(new _keyframe_iterator());
iter->ref_count=1;
iter->index=n;
iter->iter=canvas_interface()->get_canvas()->keyframe_list().begin();
while(n--)
{
if(iter->iter==canvas_interface()->get_canvas()->keyframe_list().end())
{
g_warning("KeyframeTreeStore::iter_nth_child_vfunc: >>>BUG<<< in %s on line %d",__FILE__,__LINE__);
delete iter;
clear_iterator(gtk_iter);
return false;
}
++iter->iter;
}
gtk_iter->user_data=static_cast<gpointer>(iter);
gtk_iter->stamp=stamp_;
return true;
}
bool
KeyframeTreeStore::iter_parent_vfunc (GtkTreeIter* gtk_iter, const GtkTreeIter* child)
{
dump_iterator(child,"child");
iterator_sane(child);
clear_iterator(gtk_iter);
return false;
}
*/
void
KeyframeTreeStore::ref_node_vfunc (iterator& xiter)const
{
GtkTreeIter* gtk_iter(xiter.gobj());
if(!gtk_iter || !iterator_sane(gtk_iter)) return;
_keyframe_iterator *iter(static_cast<_keyframe_iterator*>(gtk_iter->user_data));
iter->ref_count++;
}
void
KeyframeTreeStore::unref_node_vfunc (iterator& xiter)const
{
GtkTreeIter* gtk_iter(xiter.gobj());
if(!gtk_iter || !iterator_sane(gtk_iter)) return;
_keyframe_iterator *iter(static_cast<_keyframe_iterator*>(gtk_iter->user_data));
iter->ref_count--;
if(!iter->ref_count)
{
delete iter;
// Make this iterator invalid
gtk_iter->stamp=0;
}
}
Gtk::TreeModel::Path
KeyframeTreeStore::get_path_vfunc (const iterator& gtk_iter)const
{
Gtk::TreeModel::Path path;
// If this is the root node, then return
// a root path
if(!iterator_sane(gtk_iter))
return path;
_keyframe_iterator *iter(static_cast<_keyframe_iterator*>(gtk_iter->gobj()->user_data));
path.push_back(iter->index);
return path;
}
bool
KeyframeTreeStore::get_iter_vfunc (const Gtk::TreeModel::Path& path, iterator& iter)const
{
if(path.size()>=1)
return iter_nth_root_child_vfunc(path.front(),iter);
// Error case
g_warning("KeyframeTreeStore::get_iter_vfunc(): Bad path \"%s\"",path.to_string().c_str());
//clear_iterator(iter);
return false;
}
bool
KeyframeTreeStore::iter_is_valid (const iterator& iter) const
{
return iterator_sane(iter);
}
void
KeyframeTreeStore::get_value_vfunc (const Gtk::TreeModel::iterator& gtk_iter, int column, Glib::ValueBase& value)const
{
dump_iterator(gtk_iter,"gtk_iter");
if(!iterator_sane(gtk_iter))
return;
_keyframe_iterator *iter(static_cast<_keyframe_iterator*>(gtk_iter->gobj()->user_data));
switch(column)
{
case 0: // Time
{
Glib::Value<synfig::Time> x;
g_value_init(x.gobj(),x.value_type());
x.set(iter->iter->get_time());
g_value_init(value.gobj(),x.value_type());
g_value_copy(x.gobj(),value.gobj());
return;
}
case 3: // Time Delta
{
Glib::Value<synfig::Time> x;
g_value_init(x.gobj(),x.value_type());
synfig::Keyframe prev_keyframe(*iter->iter);
synfig::Keyframe keyframe;
{
KeyframeList::iterator tmp(iter->iter);
tmp++;
if(tmp==get_canvas()->keyframe_list().end())
{
x.set(Time(0));
g_value_init(value.gobj(),x.value_type());
g_value_copy(x.gobj(),value.gobj());
return;
}
keyframe=*tmp;
}
Time delta(0);
try {
delta=keyframe.get_time()-prev_keyframe.get_time();
}catch(...) { }
x.set(delta);
g_value_init(value.gobj(),x.value_type());
g_value_copy(x.gobj(),value.gobj());
return;
}
case 1: // Description
{
g_value_init(value.gobj(),G_TYPE_STRING);
g_value_set_string(value.gobj(),iter->iter->get_description().c_str());
return;
}
case 2: // Keyframe
{
Glib::Value<synfig::Keyframe> x;
g_value_init(x.gobj(),x.value_type());
x.set(*iter->iter);
g_value_init(value.gobj(),x.value_type());
g_value_copy(x.gobj(),value.gobj());
return;
}
case 4: // Active
{
Glib::Value<bool> x;
g_value_init(x.gobj(),x.value_type());
x.set(iter->iter->active());
g_value_init(value.gobj(),x.value_type());
g_value_copy(x.gobj(),value.gobj());
}
default:
break;
}
}
Gtk::TreeModel::Row
KeyframeTreeStore::find_row(const synfig::Keyframe &keyframe)
{
Gtk::TreeModel::Row row(*(children().begin()));
dump_iterator(row,"find_row,begin");
const GtkTreeIter *gtk_iter(row.gobj());
if(!iterator_sane(gtk_iter))
throw std::runtime_error(_("Unable to find Keyframe in table"));
_keyframe_iterator *iter(static_cast<_keyframe_iterator*>(gtk_iter->user_data));
synfig::KeyframeList &keyframe_list(canvas_interface()->get_canvas()->keyframe_list());
if(keyframe_list.empty())
throw std::runtime_error(_("There are no keyframes in this canvas"));
iter->index=0;
for(iter->iter=keyframe_list.begin();iter->iter!=keyframe_list.end() && *iter->iter!=keyframe;++iter->iter)
{
iter->index++;
}
if(iter->iter==keyframe_list.end())
throw std::runtime_error(_("Unable to find Keyframe in table"));
return row;
}
bool
KeyframeTreeStore::find_keyframe_path(const synfig::Keyframe &keyframe, Gtk::TreeModel::Path &path)
{
assert(keyframe);
Gtk::TreeRow row(find_row(keyframe));
path = get_path(row);
return true;
}
void
KeyframeTreeStore::add_keyframe(synfig::Keyframe keyframe)
{
try
{
Gtk::TreeRow row(find_row(keyframe));
dump_iterator(row.gobj(),"add_keyframe,row");
Gtk::TreePath path(get_path(row));
row_inserted(path,row);
old_keyframe_list=get_canvas()->keyframe_list();
//old_keyframe_list.add(keyframe);
//old_keyframe_list.sort();
}
catch(std::exception &x)
{
g_warning("%s", x.what());
}
// inform that something change around time to update the canvasview time widget color
canvas_interface()->signal_time_changed()();
}
void
KeyframeTreeStore::remove_keyframe(synfig::Keyframe keyframe)
{
try
{
if(1)
{
// Hack: (begin) the keyframe should exist in keyframe_list,
// otherwise find_row() function will fail.
// Example: try removing keyframe from composition with only 1 kf
// Note: To avoid the hack the KeyframeTreeStore probably should be re-implemented as ListStore --KD
canvas_interface()->get_canvas()->keyframe_list().add(keyframe);
Gtk::TreeRow row(find_row(keyframe));
dump_iterator(row,"remove_keyframe,row");
Gtk::TreePath path(get_path(row));
row_deleted(path);
// Hack: (end) remove added keyframe
canvas_interface()->get_canvas()->keyframe_list().erase(keyframe);
old_keyframe_list.erase(keyframe);
}
else
{
g_warning("KeyframeTreeStore::remove_keyframe: Keyframe not in table");
}
}
catch(std::exception &x)
{
g_warning("%s", x.what());
}
// inform that something change around time to update the canvasview time widget color
canvas_interface()->signal_time_changed()();
}
void
KeyframeTreeStore::change_keyframe(synfig::Keyframe keyframe)
{
try
{
Gtk::TreeRow row(find_row(keyframe));
unsigned int new_index(get_index_from_model_iter(row));
unsigned int old_index(0);
synfig::KeyframeList::iterator iter;
for(old_index=0,iter=old_keyframe_list.begin();iter!=old_keyframe_list.end() && (UniqueID)*iter!=(UniqueID)keyframe;++iter,old_index++)
;
if(iter!=old_keyframe_list.end() && new_index!=old_index)
{
std::vector<int> new_order;
for(unsigned int i=0;i<old_keyframe_list.size();i++)
{
new_order.push_back(i);
}
if(new_order.size()>new_index)
{
new_order.erase(new_order.begin()+new_index);
new_order.insert(new_order.begin()+old_index,new_index);
//new_order[old_index]=
rows_reordered (Path(), iterator(), &new_order[0]);
}
}
old_keyframe_list=get_canvas()->keyframe_list();
row=find_row(keyframe);
dump_iterator(row,"change_keyframe,row");
row_changed(get_path(row),row);
// If exist, previous row should be updated too (length value)
if (new_index != 0)
{
KeyframeList::iterator keyframe_prev;
if (get_canvas()->keyframe_list().find_prev(keyframe.get_time(), keyframe_prev, false)) {
//synfig::Keyframe keyframe_prev = *(get_canvas()->keyframe_list().find_prev(keyframe.get_time(),false));
Gtk::TreeRow row_prev(find_row(*keyframe_prev));
dump_iterator(row_prev,"change_keyframe,row_prev");
row_changed(get_path(row_prev),row_prev);
}
}
}
catch(std::exception &x)
{
g_warning("%s", x.what());
}
// inform that something change around time to update the canvasview time widget color
canvas_interface()->signal_time_changed()();
}