Blob Blame Raw
/* === S Y N F I G ========================================================= */
/*!	\file node.cpp
**	\brief Template File
**
**	$Id$
**
**	\legal
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2007, 2008 Chris Moore
**
**	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 "node.h"
// #include "nodebase.h"		// this defines a bunch of sigc::slots that are never used

#ifdef HASH_MAP_H
#include HASH_MAP_H
#else
#include <map>
#endif

#endif

/* === U S I N G =========================================================== */

using namespace std;
using namespace etl;
using namespace synfig;

/* === M A C R O S ========================================================= */

// About BE_FRUGAL_WITH_GUIDS
// If this macro is set, then a GUID will NOT
// be calculated until the first call to get_guid()
// This also means that the node doesn't get
// added to the database until get_guid() is called
// for the first time, or set_guid() is called.
// If it is expensive to calculate GUIDs, then
// this can improve performance a tad in
// some cases. Otherwise, it doesn't change
// much of anything.
#define BE_FRUGAL_WITH_GUIDS 1

#ifndef __sys_clock
#ifndef _WIN32
# include <time.h>
# define __sys_clock	::clock
#else
# ifdef __GNUG__
#  include <time.h>
#  define __sys_clock	::clock
# else
typedef int clock_t;
extern clock_t _clock();
#  define CLOCKS_PER_SEC 1000
#  define __sys_clock	_clock
# endif
#endif
#endif

/* === G L O B A L S ======================================================= */

#ifdef HASH_MAP_H
typedef HASH_MAP_CLASS<synfig::GUID,Node*,GUIDHash> GlobalNodeMap;
#else
typedef map<synfig::GUID,Node*> GlobalNodeMap;
#endif

static GlobalNodeMap* global_node_map_;

static GlobalNodeMap& global_node_map()
{
	if(!global_node_map_)
		global_node_map_=new GlobalNodeMap;
	return *global_node_map_;
}

/* === P R O C E D U R E S ================================================= */

synfig::Node*
synfig::find_node(const synfig::GUID& guid)
{
	if(global_node_map().count(guid)==0)
		return 0;
	return global_node_map()[guid];
}

static void
refresh_node(synfig::Node* node, synfig::GUID old_guid)
{
	assert(global_node_map().count(old_guid));
	global_node_map().erase(old_guid);
	assert(!global_node_map().count(old_guid));
	global_node_map()[node->get_guid()]=node;
}

/* === M E T H O D S ======================================================= */

#ifdef _DEBUG
const char *
TimePoint::c_str()const
{
	return get_time().get_string().c_str();
}
#endif

void
TimePoint::absorb(const TimePoint& x)
{
	if(get_guid()==x.get_guid())
		return;
	set_guid(get_guid()^x.get_guid());

	if(get_after()==INTERPOLATION_NIL)
		set_after(x.get_after());
	if(get_before()==INTERPOLATION_NIL)
		set_before(x.get_before());

	if(get_after()!=x.get_after() && x.get_after()!=INTERPOLATION_NIL)
		set_after(INTERPOLATION_UNDEFINED);
	if(get_before()!=x.get_before() && x.get_before()!=INTERPOLATION_NIL)
		set_before(INTERPOLATION_UNDEFINED);
}

TimePointSet::iterator
TimePointSet::insert(const TimePoint& x)
{
	iterator iter(find(x));
	if(iter!=end())
	{
		const_cast<TimePoint&>(*iter).absorb(x);
		return iter;
	}
	return std::set<TimePoint>::insert(x).first;
}















Node::Node():
	guid_(0),
	bchanged(true),
	time_last_changed_(__sys_clock()),
	deleting_(false)
{
#ifndef BE_FRUGAL_WITH_GUIDS
	guid_.make_unique();
	assert(guid_);
	assert(!global_node_map().count(guid_));
	global_node_map()[guid_]=this;
#endif
}

Node::~Node()
{
	begin_delete();

	if(guid_)
	{
		assert(global_node_map().count(guid_));
		global_node_map().erase(guid_);
		assert(!global_node_map().count(guid_));
	}
}

void
Node::changed()
{
	time_last_changed_=__sys_clock();
	on_changed();
}


//! Gets the GUID for this value node
const synfig::GUID&
Node::get_guid()const
{
#ifdef BE_FRUGAL_WITH_GUIDS
	if(!guid_)
	{
		const_cast<synfig::GUID&>(guid_).make_unique();
		assert(guid_);
		assert(!global_node_map().count(guid_));
		global_node_map()[guid_]=const_cast<Node*>(this);
	}
#endif

	return guid_;
}

//! Sets the GUID for this value node
void
Node::set_guid(const synfig::GUID& x)
{
	assert(x);

#ifdef BE_FRUGAL_WITH_GUIDS
	if(!guid_)
	{
		guid_=x;
		assert(!global_node_map().count(guid_));
		global_node_map()[guid_]=this;
	}
	else
#endif
	if(guid_!=x)
	{
		synfig::GUID oldguid(guid_);
		guid_=x;
		refresh_node(this, oldguid);
		on_guid_changed(oldguid);
	}
}

int
Node::get_time_last_changed()const
{
	return time_last_changed_;
}

void
Node::add_child(Node*x)
{
	x->parent_set.insert(this);
}

void
Node::remove_child(Node*x)
{
	if(x->parent_set.count(this)) x->parent_set.erase(this);
}

int
Node::parent_count()const
{
	return parent_set.size();
}

const Node::time_set &
Node::get_times() const
{
	if(bchanged)
	{
		times.clear();
		get_times_vfunc(times);
		bchanged = false;
	}

	//set the output set...
	return times;
}

void
Node::begin_delete()
{
	if(!deleting_)
	{
		deleting_=true; signal_deleted()();
	}
}

void
Node::on_changed()
{
	bchanged = true;
	signal_changed()();

	std::set<Node*>::iterator iter;
	for(iter=parent_set.begin();iter!=parent_set.end();++iter)
	{
		(*iter)->changed();
	}
}

void
Node::on_guid_changed(synfig::GUID guid)
{
	signal_guid_changed()(guid);
}