Blob Blame Raw


// TnzCore includes
#include "tconst.h"
#include "tutil.h"
#include "tstream.h"

// TnzBase includes
#include "tparamcontainer.h"
#include "tfxattributes.h"
#include "texternfx.h"
#include "tpassivecachemanager.h"

// STD includes
#include <set>

#include "tfx.h"

//==============================================================================

TIStream &operator>>(TIStream &in, TFxP &p)
{
	TPersist *tmp = 0;
	in >> tmp;
	p = TFxP(dynamic_cast<TFx *>(tmp));
	return in;
}

//==============================================================================
//
// namespace {}
//
//------------------------------------------------------------------------------

namespace
{

//------------------------------------------------------------------------------

typedef pair<string, TFxPort *> NamePort;
typedef map<string, TFxPort *> PortTable;
typedef vector<NamePort> PortArray;

//------------------------------------------------------------------------------

class PortNameEq
{
	string m_name;

public:
	PortNameEq(const string &name) : m_name(name) {}
	~PortNameEq() {}
	bool operator()(const NamePort &np) { return np.first == m_name; }
};

//------------------------------------------------------------------------------

void skipChild(TIStream &is)
{
	while (!is.eos()) {
		string dummy = is.getString();
		string tagName;
		while (is.openChild(tagName)) {
			skipChild(is);
			if (is.isBeginEndTag())
				is.matchTag(tagName);
			is.closeChild();
		}
	}
}

//------------------------------------------------------------------------------

TPointD updateDagPosition(const TPointD &pos, const VersionNumber &tnzVersion)
{
	if (tnzVersion < VersionNumber(1, 16))
		return TConst::nowhere;
	return pos;
}

} // namespace

//==============================================================================
//
// TFxParamChange
//
//------------------------------------------------------------------------------

TFxParamChange::TFxParamChange(TFx *fx, double firstAffectedFrame, double lastAffectedFrame, bool dragging)
	: TFxChange(fx, firstAffectedFrame, lastAffectedFrame, dragging)
{
}

//--------------------------------------------------

TFxParamChange::TFxParamChange(TFx *fx, const TParamChange &src)
	: TFxChange(fx, src.m_firstAffectedFrame, src.m_lastAffectedFrame, src.m_dragging)
{
}

//==============================================================================
//
// TFxChange
//
//------------------------------------------------------------------------------

double TFxChange::m_minFrame = -(std::numeric_limits<double>::max)();
double TFxChange::m_maxFrame = +(std::numeric_limits<double>::max)();

//==============================================================================
//
// TFxPortDynamicGroup
//
//------------------------------------------------------------------------------

TFxPortDynamicGroup::TFxPortDynamicGroup(const std::string &prefix, int minSize)
	: m_portsPrefix(prefix), m_minPortsCount(minSize)
{
}

//--------------------------------------------------

TFxPortDynamicGroup::~TFxPortDynamicGroup()
{
	clear();
}

//--------------------------------------------------

void TFxPortDynamicGroup::addPort(TFxPort *port)
{
	m_ports.push_back(port);
}

//--------------------------------------------------

void TFxPortDynamicGroup::removePort(TFxPort *port)
{
	m_ports.resize(std::remove(m_ports.begin(), m_ports.end(), port) - m_ports.begin());
	delete port;
}

//--------------------------------------------------

void TFxPortDynamicGroup::clear()
{
	std::for_each(m_ports.begin(), m_ports.end(), TDeleteObjectFunctor());
	m_ports.clear();
}

//==============================================================================
//
// TFxImp
//
//------------------------------------------------------------------------------

class TFxImp
{
public:
	TFx *m_fx;				 //!< Fx back-pointer
	TFxImp *m_prev, *m_next; //!< Linked fxs

	wstring m_name;
	wstring m_fxId;

	PortTable m_portTable; //!< Name -> port  map
	PortArray m_portArray; //!< Ports container

	TParamContainer m_paramContainer;

	std::set<TFxPort *> m_outputPort;
	TFxTimeRegion m_activeTimeRegion;
	std::set<TFxObserver *> m_observers;

	TFxAttributes m_attributes;

	static unsigned long m_nextId;
	unsigned long m_id; //!< Unique fx identifier, per Toonz session.
	//!< It is intended to be used \b solely to build
	//!< an internal string description of the fx.
public:
	TFxImp(TFx *fx)
		: m_fx(fx), m_activeTimeRegion(TFxTimeRegion::createUnlimited()), m_id()
	{
		m_prev = m_next = this;
	}

	~TFxImp()
	{
		m_prev->m_next = m_next;
		m_next->m_prev = m_prev;
	}

	bool checkLinks() const
	{
		assert(m_prev);
		assert(m_next);
		assert(m_prev->m_next == this);
		assert(m_next->m_prev == this);

		return true;
	}

private:
	// not copyable
	TFxImp(const TFxImp &);
	TFxImp &operator=(const TFxImp &);
};

//--------------------------------------------------

unsigned long TFxImp::m_nextId = 0;

//==============================================================================
//
// TFxFactory
//
//------------------------------------------------------------------------------

class TFxFactory // singleton
{
	typedef std::map<std::string,
					 std::pair<TFxInfo, TFxDeclaration *>> Table;

	Table m_table;
	std::vector<string> m_map;

	TFxFactory() {}

public:
	static TFxFactory *instance()
	{
		static TFxFactory _instance;
		return &_instance;
	}

	void add(const TFxInfo &info, TFxDeclaration *decl)
	{
		// check for dups ???
		std::pair<TFxInfo, TFxDeclaration *> p(info, decl);
		m_table[info.m_name] = p;
	}

	TFx *create(std::string name)
	{
		Table::iterator it = m_table.find(name);
		if (it != m_table.end()) {
			TFxDeclaration *decl = it->second.second;

			TPersist *obj = decl->create();
			assert(obj);

			return dynamic_cast<TFx *>(obj);
		} else
			return TExternFx::create(name);
	}

	void getFxInfos(std::vector<TFxInfo> &info) const
	{
		for (Table::const_iterator it = m_table.begin(); it != m_table.end(); ++it)
			info.push_back(it->second.first);
	}

	TFxInfo getFxInfo(const std::string &fxIdentifier) const
	{
		Table::const_iterator it = m_table.find(fxIdentifier);
		return (it != m_table.end()) ? it->second.first : TFxInfo();
	}
};

//==============================================================================
//
// TFxDeclaration
//
//------------------------------------------------------------------------------

TFxDeclaration::TFxDeclaration(const TFxInfo &info)
	: TPersistDeclaration(info.m_name)
{
	TFxFactory::instance()->add(info, this);
}

//==============================================================================
//
// TFx
//
//------------------------------------------------------------------------------

DEFINE_CLASS_CODE(TFx, 3)

//------------------------------------------------------------------------------

TFx::TFx()
	: TSmartObject(m_classCode) // TPersist(TFxImp::m_instances)
	, m_imp(new TFxImp(this))
{
}

//--------------------------------------------------

TFx::~TFx()
{
	for (std::set<TFxPort *>::iterator it = m_imp->m_outputPort.begin();
		 it != m_imp->m_outputPort.end(); ++it) {
		TFxPort *port = *it;
		port->setFx(0);
	}

	delete m_imp;
}

//--------------------------------------------------

wstring TFx::getName() const
{
	return m_imp->m_name;
}

//--------------------------------------------------

void TFx::setName(wstring name)
{
	m_imp->m_name = name;
}

//--------------------------------------------------

wstring TFx::getFxId() const
{
	return m_imp->m_fxId;
}

//--------------------------------------------------

void TFx::setFxId(wstring id)
{
	m_imp->m_fxId = id;
}

//--------------------------------------------------

TFxAttributes *TFx::getAttributes() const
{
	return &m_imp->m_attributes;
}

//--------------------------------------------------

const TParamContainer *TFx::getParams() const
{
	return &m_imp->m_paramContainer;
}

//--------------------------------------------------

TParamContainer *TFx::getParams()
{
	return &m_imp->m_paramContainer;
}

//--------------------------------------------------

TFx *TFx::clone(bool recursive) const
{
	TFx *fx = TFx::create(getFxType());
	assert(fx);
	return this->clone(fx, recursive);
}

TFx *TFx::clone(TFx *fx, bool recursive) const
{
	// Copy misc stuff

	fx->m_imp->m_activeTimeRegion = m_imp->m_activeTimeRegion;
	fx->setIdentifier(getIdentifier());
	fx->getParams()->copy(getParams());
	fx->setFxId(getFxId());
	fx->setName(getName());

	// Copy attributes
	*fx->getAttributes() = *getAttributes();

	// Clone the dynamic ports pool
	if (hasDynamicPortGroups()) {
		int p, pCount = m_imp->m_portArray.size();
		for (p = 0; p != pCount; ++p) {
			const NamePort &namedPort = m_imp->m_portArray[p];

			int groupIdx = namedPort.second->getGroupIndex();
			if (groupIdx >= 0 && !fx->getInputPort(namedPort.first))
				fx->addInputPort(namedPort.first, new TRasterFxPort, groupIdx);
		}

		assert(pCount == fx->getInputPortCount());
	}

	// copia ricorsiva sulle porte
	if (recursive) {
		int p, pCount = getInputPortCount();
		for (p = 0; p != pCount; ++p) {
			TFxPort *port = getInputPort(p);
			if (port->getFx())
				fx->connect(getInputPortName(p), port->getFx()->clone(true));
		}
	}

	return fx;
}

//--------------------------------------------------

void TFx::linkParams(TFx *fx)
{
	if (this == fx)
		return;
	getParams()->link(fx->getParams());
	m_imp->m_activeTimeRegion = fx->m_imp->m_activeTimeRegion;

	// aggiorno i link
	assert(m_imp->checkLinks());
	assert(fx->m_imp->checkLinks());

	tswap(m_imp->m_next, fx->m_imp->m_next);
	tswap(m_imp->m_next->m_prev, fx->m_imp->m_next->m_prev);

	assert(m_imp->checkLinks());
	assert(fx->m_imp->checkLinks());
}

//--------------------------------------------------

void TFx::unlinkParams()
{
	// clone dei parametri
	getParams()->unlink();

	assert(m_imp->m_prev);
	assert(m_imp->m_next);
	assert(m_imp->m_prev->m_next == m_imp);
	assert(m_imp->m_next->m_prev == m_imp);
	m_imp->m_prev->m_next = m_imp->m_next;
	m_imp->m_next->m_prev = m_imp->m_prev;
	m_imp->m_next = m_imp->m_prev = m_imp;

	notify(TFxParamsUnlinked(this));
}

//--------------------------------------------------

bool TFx::addInputPort(const std::string &name, TFxPort &port)
{
	PortTable::iterator it = m_imp->m_portTable.find(name);
	if (it != m_imp->m_portTable.end())
		return false;

	m_imp->m_portTable[name] = &port;
	m_imp->m_portArray.push_back(NamePort(name, &port));
	port.setOwnerFx(this); // back pointer to the owner...

	return true;
}

//--------------------------------------------------

bool TFx::addInputPort(const std::string &name, TFxPort *port, int groupIndex)
{
	if (!port) {
		assert(port);
		return false;
	}

	if (groupIndex >= dynamicPortGroupsCount()) {
		assert(groupIndex < dynamicPortGroupsCount());
		return false;
	}

	if (!addInputPort(name, *port))
		return false;

	// Assign the port to the associated group
	port->m_groupIdx = groupIndex;

	TFxPortDG *group = const_cast<TFxPortDG *>(dynamicPortGroup(groupIndex));
	group->addPort(port);

	assert(name.find(group->m_portsPrefix) == 0);

	return true;
}

//--------------------------------------------------

bool TFx::removeInputPort(const std::string &name)
{
	m_imp->m_portTable.erase(name);

	PortArray::iterator it = std::find_if(m_imp->m_portArray.begin(), m_imp->m_portArray.end(), PortNameEq(name));
	if (it == m_imp->m_portArray.end())
		return false;

	TFxPort *port = it->second;
	port->setOwnerFx(0);

	if (port->m_groupIdx >= 0) {
		TFxPortDG *group = const_cast<TFxPortDG *>(this->dynamicPortGroup(port->m_groupIdx));
		group->removePort(port); // The port is DELETED here
	}

	m_imp->m_portArray.erase(it);
	return true;
}

//--------------------------------------------------

namespace
{
struct IsPrefix {
	const std::string &m_prefix;
	bool operator()(const NamePort &nameport)
	{
		return (strncmp(m_prefix.c_str(), nameport.first.c_str(), m_prefix.size()) == 0);
	}
};
} // namespace

void TFx::clearDynamicPortGroup(int g)
{
	TFxPortDG *dg = const_cast<TFxPortDG *>(this->dynamicPortGroup(g));

	const std::string &prefix = dg->portsPrefix();

	std::string prefixEnd = prefix;
	++prefixEnd[prefixEnd.size() - 1];

	{
		// Delete all associated ports from the ports table
		PortTable::iterator pBegin(m_imp->m_portTable.lower_bound(prefix));
		PortTable::iterator pEnd(m_imp->m_portTable.lower_bound(prefixEnd));

		m_imp->m_portTable.erase(pBegin, pEnd);

		// Traverse the ports array and remove all ports in the group
		IsPrefix func = {prefix};
		m_imp->m_portArray.resize(std::remove_if(m_imp->m_portArray.begin(), m_imp->m_portArray.end(), func) - m_imp->m_portArray.begin());
	}

	dg->clear(); // Has ports ownership, so deletes them
}

//--------------------------------------------------

bool TFx::addOutputConnection(TFxPort *port)
{
	assert(port->getFx() == this);
	return m_imp->m_outputPort.insert(port).second;
}

//--------------------------------------------------

bool TFx::removeOutputConnection(TFxPort *port)
{
	std::set<TFxPort *>::iterator it = m_imp->m_outputPort.find(port);
	if (it == m_imp->m_outputPort.end())
		return false;

	m_imp->m_outputPort.erase(it);
	return true;
}

//--------------------------------------------------

int TFx::getOutputConnectionCount() const
{
	return m_imp->m_outputPort.size();
}

//--------------------------------------------------

TFxPort *TFx::getOutputConnection(int i) const
{
	assert(0 <= i && i <= (int)m_imp->m_outputPort.size());
	std::set<TFxPort *>::iterator it = m_imp->m_outputPort.begin();
	std::advance(it, i);
	if (it == m_imp->m_outputPort.end())
		return 0;
	return *it;
}

//--------------------------------------------------

bool TFx::disconnect(const std::string &name)
{
	TFxPort *port = getInputPort(name);
	if (!port)
		return false;

	port->setFx(0);
	return true;
}

//--------------------------------------------------

bool TFx::connect(const std::string &name, TFx *fx)
{
	TFxPort *port = getInputPort(name);
	if (!port)
		return false;

	port->setFx(fx);
	return true;
}

//--------------------------------------------------

int TFx::getInputPortCount() const
{
	return m_imp->m_portArray.size();
}

//--------------------------------------------------

TFxPort *TFx::getInputPort(int index) const
{
	assert(0 <= index && index < (int)m_imp->m_portArray.size());
	return m_imp->m_portArray[index].second;
}

//--------------------------------------------------

string TFx::getInputPortName(int index) const
{
	assert(0 <= index && index < (int)(m_imp->m_portArray.size()));
	return m_imp->m_portArray[index].first;
}

//--------------------------------------------------

bool TFx::renamePort(const string &oldName, const string &newName)
{
	PortTable::iterator it = m_imp->m_portTable.find(oldName);
	if (it == m_imp->m_portTable.end())
		return false;

	TFxPort *port = it->second;
	m_imp->m_portTable.erase(it);
	m_imp->m_portTable[newName] = port;

	PortArray::iterator it2;
	for (it2 = m_imp->m_portArray.begin(); it2 != m_imp->m_portArray.end(); ++it2) {
		if (it2->first != oldName)
			continue;

		it2->first = newName;
		break;
	}

	return true;
}

//--------------------------------------------------

TFxPort *TFx::getInputPort(const std::string &name) const
{
	PortTable::iterator it = m_imp->m_portTable.find(name);
	if (it == m_imp->m_portTable.end())
		return 0;

	return m_imp->m_portTable[name];
}

//--------------------------------------------------

int TFx::getReferenceColumnIndex() const
{
	if (!m_imp->m_portArray.empty()) {
		TFx *fx = m_imp->m_portArray[0].second->getFx();
		if (fx)
			return fx->getReferenceColumnIndex();
	}
	return -1;
}

//--------------------------------------------------

TFxTimeRegion TFx::getTimeRegion() const
{
	if (m_imp->m_portTable.empty())
		return TFxTimeRegion::createUnlimited();

	TFxTimeRegion tr((std::numeric_limits<double>::max)(), -(std::numeric_limits<double>::max)());

	PortTable::iterator it = m_imp->m_portTable.begin();
	for (; it != m_imp->m_portTable.end(); ++it) {
		TFxPort *port = it->second;
		if (port && port->isConnected() && !port->isaControlPort()) {
			TFx *fx = port->getFx();
			assert(fx);
			tr += fx->getTimeRegion();
		}
	}

	return tr;
}

//--------------------------------------------------

void TFx::setActiveTimeRegion(const TFxTimeRegion &tr)
{
	m_imp->m_activeTimeRegion = tr;
}

//--------------------------------------------------

TFxTimeRegion TFx::getActiveTimeRegion() const
{
	return m_imp->m_activeTimeRegion;
}

//--------------------------------------------------

void TFx::onChange(const TParamChange &c)
{
	TFxParamChange change(this, c);
	notify(change);
}

//--------------------------------------------------

void TFx::addObserver(TFxObserver *obs)
{
	m_imp->m_observers.insert(obs);
}

//--------------------------------------------------

void TFx::removeObserver(TFxObserver *obs)
{
	m_imp->m_observers.erase(obs);
}

//--------------------------------------------------

void TFx::notify(const TFxChange &change)
{
	for (std::set<TFxObserver *>::iterator it = m_imp->m_observers.begin();
		 it != m_imp->m_observers.end(); ++it)
		(*it)->onChange(change);
}

//--------------------------------------------------

void TFx::notify(const TFxPortAdded &change)
{
	for (std::set<TFxObserver *>::iterator it = m_imp->m_observers.begin();
		 it != m_imp->m_observers.end(); ++it)
		(*it)->onChange(change);
}

//--------------------------------------------------

void TFx::notify(const TFxPortRemoved &change)
{
	for (std::set<TFxObserver *>::iterator it = m_imp->m_observers.begin();
		 it != m_imp->m_observers.end(); ++it)
		(*it)->onChange(change);
}

//--------------------------------------------------

void TFx::notify(const TFxParamAdded &change)
{
	for (std::set<TFxObserver *>::iterator it = m_imp->m_observers.begin();
		 it != m_imp->m_observers.end(); ++it)
		(*it)->onChange(change);
}

//--------------------------------------------------

void TFx::notify(const TFxParamRemoved &change)
{
	for (std::set<TFxObserver *>::iterator it = m_imp->m_observers.begin();
		 it != m_imp->m_observers.end(); ++it)
		(*it)->onChange(change);
}

//--------------------------------------------------

unsigned long TFx::getIdentifier() const
{
	return m_imp->m_id;
}

//--------------------------------------------------

void TFx::setIdentifier(unsigned long id)
{
	m_imp->m_id = id;
}

//--------------------------------------------------

void TFx::setNewIdentifier()
{
	m_imp->m_id = ++m_imp->m_nextId;
}

//--------------------------------------------------

void TFx::loadData(TIStream &is)
{
	string tagName;
	VersionNumber tnzVersion = is.getVersion();

	QList<int> groupIds;
	QList<wstring> groupNames;
	while (is.openChild(tagName)) {
		if (tagName == "params") {
			while (!is.eos()) {
				string paramName;
				while (is.openChild(paramName)) {
					TParamP param = getParams()->getParam(paramName);
					if (param)
						param->loadData(is);
					else // il parametro non e' presente -> skip
						skipChild(is);

					is.closeChild();
				}
			}
		} else if (tagName == "paramsLinkedTo") {
			TPersist *p = 0;
			is >> p;
			TFx *fx = dynamic_cast<TFx *>(p);
			if (fx == 0)
				throw TException("Missing linkedSetRoot");
			linkParams(fx);
		} else if (tagName == "ports") {
			string portName;
			while (!is.eos()) {
				while (is.openChild(portName)) {
					VersionNumber version = is.getVersion();
					compatibilityTranslatePort(version.first, version.second, portName);

					// Try to find the specified port
					TFxPort *port = getInputPort(portName);
					if (!port) {
						// Scan dynamic port groups for a containing one - add a port there if found
						int g, gCount = dynamicPortGroupsCount();
						for (g = 0; g != gCount; ++g) {
							TFxPortDG *group = const_cast<TFxPortDG *>(dynamicPortGroup(g));

							if (group->contains(portName)) {
								addInputPort(portName, port = new TRasterFxPort, g);
								break;
							}
						}
					}

					// Could not find (or add) a port with the corresponding name - throw
					if (!port)
						throw TException(string("port '") + portName + "' is not present");

					if (!is.eos()) {
						TPersist *p = 0;
						is >> p;
						TFx *fx = dynamic_cast<TFx *>(p);
						port->setFx(fx);
					}

					is.closeChild();
				}
			}
		} else if (tagName == "dagNodePos") {
			TPointD p;
			is >> p.x >> p.y;
			m_imp->m_attributes.setDagNodePos(updateDagPosition(p, tnzVersion));
		} else if (tagName == "numberId") {
			int numberId = 0;
			is >> numberId;
			m_imp->m_attributes.setId(numberId);
		} else if (tagName == "passiveCacheId") {
			int passiveCacheId = 0;
			is >> passiveCacheId;

			assert(passiveCacheId > 0);
			TPassiveCacheManager::instance()->declareCached(this, passiveCacheId);
		} else if (tagName == "name") {
			// passo attraverso un filepath solo per evitare i problemi di blank
			// o caratteri strani dentro il nome (sospetto che tfilepath sia gestito
			// correttamente mentre wstring no
			TFilePath tmp;
			is >> tmp;
			setName(tmp.getWideName());
		} else if (tagName == "fxId") {
			TFilePath tmp;
			is >> tmp;
			setFxId(tmp.getWideName());
		} else if (tagName == "enabled") {
			int flag = 1;
			is >> flag;
			m_imp->m_attributes.enable(flag != 0);
		} else if (tagName == "opened") {
			int opened = 1;
			is >> opened;
			m_imp->m_attributes.setIsOpened(opened);
		} else if (tagName == "groupIds") {
			int groupId;
			while (!is.eos()) {
				is >> groupId;
				groupIds.append(groupId);
			}
		} else if (tagName == "groupNames") {
			wstring groupName;
			while (!is.eos()) {
				is >> groupName;
				groupNames.append(groupName);
			}
		} else {
			throw TException("Unknown tag!");
		}
		is.closeChild();
	}
	if (!groupIds.isEmpty()) {
		assert(groupIds.size() == groupNames.size());
		int i;
		for (i = 0; i < groupIds.size(); i++) {
			m_imp->m_attributes.setGroupId(groupIds[i]);
			m_imp->m_attributes.setGroupName(groupNames[i]);
		}
	}
}

//--------------------------------------------------

void TFx::saveData(TOStream &os)
{
	TFx *linkedSetRoot = this;
	if (m_imp->m_next != m_imp) {
		TFxImp *imp = m_imp->m_next;
		int guard = 0;
		while (guard++ < 1000 && imp != m_imp) {
			if (imp->m_fx < linkedSetRoot)
				linkedSetRoot = imp->m_fx;
			imp = imp->m_next;
		}
		assert(imp == m_imp);
		assert(linkedSetRoot);
	}
	if (linkedSetRoot == this) {
		os.openChild("params");
		for (int i = 0; i < getParams()->getParamCount(); i++) {
			string paramName = getParams()->getParamName(i);
			TParam *param = getParams()->getParam(i);
			os.openChild(paramName);
			param->saveData(os);
			os.closeChild();
		}
		os.closeChild();
	} else {
		os.openChild("paramsLinkedTo");
		os << linkedSetRoot;
		os.closeChild();
	}

	os.openChild("ports");
	for (PortTable::iterator pit = m_imp->m_portTable.begin();
		 pit != m_imp->m_portTable.end(); ++pit) {
		os.openChild(pit->first);
		if (pit->second->isConnected())
			os << TFxP(pit->second->getFx()).getPointer();
		os.closeChild();
	}
	os.closeChild();

	TPointD p = m_imp->m_attributes.getDagNodePos();
	if (p != TConst::nowhere)
		os.child("dagNodePos") << p.x << p.y;
	int numberId = m_imp->m_attributes.getId();
	os.child("numberId") << numberId;
	bool cacheEnabled = TPassiveCacheManager::instance()->cacheEnabled(this);
	if (cacheEnabled)
		os.child("passiveCacheId") << TPassiveCacheManager::instance()->getPassiveCacheId(this);
	wstring name = getName();
	if (name != L"")
		os.child("name") << TFilePath(name);
	wstring fxId = getFxId();
	os.child("fxId") << fxId;
	if (!m_imp->m_attributes.isEnabled())
		os.child("enabled") << 0;
	os.child("opened") << (int)m_imp->m_attributes.isOpened();
	if (m_imp->m_attributes.isGrouped()) {
		os.openChild("groupIds");
		QStack<int> groupIdStack = m_imp->m_attributes.getGroupIdStack();
		int i;
		for (i = 0; i < groupIdStack.size(); i++)
			os << groupIdStack[i];
		os.closeChild();
		os.openChild("groupNames");
		QStack<wstring> groupNameStack = m_imp->m_attributes.getGroupNameStack();
		for (i = 0; i < groupNameStack.size(); i++)
			os << groupNameStack[i];
		os.closeChild();
	}
}

//--------------------------------------------------

void TFx::loadPreset(TIStream &is)
{
	string tagName;
	while (is.openChild(tagName)) {
		if (tagName == "dvpreset") {
			string fxId = is.getTagAttribute("fxId");
			if (fxId != getFxType())
				throw TException("Preset doesn't match the fx type");
		} else if (tagName == "params") {
			while (!is.eos()) {
				string paramName;
				while (is.openChild(paramName)) {
					try {
						TParamP param = getParams()->getParam(paramName);
						param->loadData(is);
					} catch (TException &) { /*skip*/
					}						 // il parametro non e' presente
					is.closeChild();
				}
			}
		} else {
			throw TException("Fx preset unknown tag!");
		}
	}
}

//--------------------------------------------------

void TFx::savePreset(TOStream &os)
{
	map<string, string> attributes;
	attributes.insert(std::make_pair(string("ver"), string("1.0")));
	attributes.insert(std::make_pair(string("fxId"), getFxType()));

	os.openChild("dvpreset", attributes);

	os.openChild("params");
	for (int i = 0; i < getParams()->getParamCount(); i++) {
		string paramName = getParams()->getParamName(i);
		TParam *param = getParams()->getParam(i);
		os.openChild(paramName);
		param->saveData(os);
		os.closeChild();
	}
	os.closeChild();

	os.closeChild();
}

//--------------------------------------------------

void TFx::disconnectAll()
{
	int p, pCount = getInputPortCount();
	for (p = 0; p != pCount; ++p)
		getInputPort(p)->setFx(0);
}

//--------------------------------------------------

TFx *TFx::create(string name)
{
	return TFxFactory::instance()->create(name);
}

//--------------------------------------------------

void TFx::listFxs(std::vector<TFxInfo> &info)
{
	TFxFactory::instance()->getFxInfos(info);
}

//--------------------------------------------------

TFxInfo TFx::getFxInfo(const std::string &fxIdentifier)
{
	return TFxFactory::instance()->getFxInfo(fxIdentifier);
}

//--------------------------------------------------

TFx *TFx::getLinkedFx() const
{
	assert(m_imp->m_next);
	assert(m_imp->m_next->m_prev == m_imp);
	assert(m_imp->m_next->m_fx != 0);
	return m_imp->m_next->m_fx;
}

//===================================================
//
// TFxTimeRegion
//
//--------------------------------------------------

//! Creates an unlimited time region.
TFxTimeRegion::TFxTimeRegion()
	: m_start((std::numeric_limits<double>::max)()), m_end(-(std::numeric_limits<double>::max)())
{
}

//--------------------------------------------------

//! Creates a time region with specified start (included) and end (\b excluded).
TFxTimeRegion::TFxTimeRegion(double start, double end)
	: m_start(start), m_end(end)
{
}

//--------------------------------------------------

TFxTimeRegion TFxTimeRegion::createUnlimited()
{
	return TFxTimeRegion(
		-(std::numeric_limits<double>::max)(),
		(std::numeric_limits<double>::max)());
}

//--------------------------------------------------

bool TFxTimeRegion::contains(double time) const
{
	return (m_start <= time && time < m_end);
}

//--------------------------------------------------

bool TFxTimeRegion::isUnlimited() const
{
	return (m_start == -(std::numeric_limits<double>::max)() ||
			m_end == (std::numeric_limits<double>::max)());
}

//--------------------------------------------------

bool TFxTimeRegion::isEmpty() const
{
	return (m_end <= m_start);
}

//--------------------------------------------------

bool TFxTimeRegion::getFrameCount(int &count) const
{
	if (isUnlimited())
		return false;
	count = tfloor(m_end) - tceil(m_start);
	return true;
}

//--------------------------------------------------

int TFxTimeRegion::getFirstFrame() const
{
	return tceil(m_start);
}

//--------------------------------------------------

int TFxTimeRegion::getLastFrame() const
{
	if (m_end == (std::numeric_limits<double>::max)())
		return (std::numeric_limits<int>::max)();
	else
		return tceil(m_end) - 1;
}

//===================================================