Blob Blame Raw


#include "toonzqt/fxschematicscene.h"

// TnzQt includes
#include "toonzqt/fxschematicnode.h"
#include "toonzqt/gutil.h"
#include "toonzqt/dvdialog.h"
#include "toonzqt/fxselection.h"
#include "toonzqt/schematicgroupeditor.h"
#include "toonzqt/swatchviewer.h"
#include "toonzqt/tselectionhandle.h"

// TnzLib includes
#include "toonz/txsheet.h"
#include "toonz/toonzscene.h"
#include "toonz/tcolumnfxset.h"
#include "toonz/fxdag.h"
#include "toonz/txshlevelcolumn.h"
#include "toonz/tcolumnfx.h"
#include "toonz/txshpalettecolumn.h"
#include "toonz/txshzeraryfxcolumn.h"
#include "toonz/fxcommand.h"
#include "toonz/txsheethandle.h"
#include "toonz/tfxhandle.h"
#include "toonz/tscenehandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/tframehandle.h"
#include "toonz/tobjecthandle.h"

// TnzBase includes
#include "tmacrofx.h"
#include "fxdata.h"
#include "tpassivecachemanager.h"
#include "tfxattributes.h"

// TnzCore includes
#include "tconst.h"

// Qt includes
#include <QMenu>
#include <QApplication>
#include <QGraphicsSceneContextMenuEvent>

namespace
{

class MatchesFx
{
	TFxP m_fx;

public:
	MatchesFx(const TFxP &fx) : m_fx(fx) {}

	bool operator()(const TFxP &fx)
	{
		TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(fx.getPointer());
		return (m_fx.getPointer() == fx.getPointer()) || (zfx && m_fx.getPointer() == zfx->getZeraryFx());
	}
};

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

void keepSubgroup(QMap<int, QList<SchematicNode *>> &editedGroup)
{
	QMap<int, QList<SchematicNode *>>::iterator it;
	for (it = editedGroup.begin(); it != editedGroup.end(); it++) {
		QList<SchematicNode *> groupedNodes = it.value();
		int i;
		for (i = 0; i < groupedNodes.size(); i++) {
			FxSchematicNode *node = dynamic_cast<FxSchematicNode *>(groupedNodes[i]);
			if (!node)
				continue;
			TFx *fx = node->getFx();
			assert(fx);
			QMap<int, QList<SchematicNode *>>::iterator it2;
			for (it2 = editedGroup.begin(); it2 != editedGroup.end(); it2++) {
				if (fx->getAttributes()->isContainedInGroup(it2.key()) &&
					!editedGroup[it2.key()].contains(node))
					editedGroup[it2.key()].append(node);
			}
		}
	}
}

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

//! Find the input and the output fx contained in \b visitedFxs.
//! \b visitedFxs must be a connected fx selection. In \b outputFx is put the root of the connected fx selection.
//! In \b inputFx is put a leaf of the connected fx selection.
void findBoundariesFxs(TFx *&inputFx, TFx *&outputFx, QMap<TFx *, bool> &visitedFxs, TFx *currentFx = 0)
{
	if (visitedFxs.isEmpty())
		return;
	if (!currentFx)
		currentFx = visitedFxs.begin().key();
	int inputPortCount = currentFx->getInputPortCount();
	int outputConnectionCount = currentFx->getOutputConnectionCount();

	if (inputPortCount > 0 && !visitedFxs[currentFx]) {
		visitedFxs[currentFx] = true;
		TFxPort *fxPort = currentFx->getInputPort(0);
		TFx *fx = fxPort->getFx();
		if (fx && visitedFxs.count(fx) == 1) {
			if (!visitedFxs[fx])
				findBoundariesFxs(inputFx, outputFx, visitedFxs, fx);
		} else
			inputFx = currentFx;
	} else
		inputFx = currentFx;
	if (!outputFx) {
		if (outputConnectionCount > 0) {
			TFx *fx = currentFx->getOutputConnection(0)->getOwnerFx();
			if (fx && visitedFxs.count(fx) == 1) {
				if (!visitedFxs[fx])
					findBoundariesFxs(inputFx, outputFx, visitedFxs, fx);
			} else
				outputFx = currentFx;
		} else
			outputFx = currentFx;
	}
}

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

bool canDisconnectSelection(FxSelection *selection)
{
	QList<TFxP> selectedFx = selection->getFxs();
	int i;
	for (i = 0; i < selectedFx.size(); i++) // .....
	{
		TFx *fx = selectedFx[i].getPointer();

		TLevelColumnFx *lcFx = dynamic_cast<TLevelColumnFx *>(fx);
		TPaletteColumnFx *pfx = dynamic_cast<TPaletteColumnFx *>(fx);
		TXsheetFx *xfx = dynamic_cast<TXsheetFx *>(fx);
		TOutputFx *ofx = dynamic_cast<TOutputFx *>(fx);
		TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(fx);

		return (!lcFx && !pfx && !xfx && !ofx && (!zfx || zfx->getInputPortCount() > 0)); // !!!!!
	}
	return false;
}

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

QList<TFxP> getRoots(const QList<TFxP> &fxs, TFxSet *terminals)
{
	QList<TFxP> roots;
	int i;
	for (i = 0; i < fxs.size(); i++) {
		TFx *fx = fxs[i].getPointer();
		int j;
		bool isRoot = true;
		for (j = 0; j < fx->getOutputConnectionCount(); j++) {
			TFx *outFx = fx->getOutputConnection(j)->getOwnerFx();
			if (outFx && std::find_if(fxs.begin(), fxs.end(), MatchesFx(outFx)) != fxs.end() &&
				!terminals->containsFx(fx))
				isRoot = false;
		}
		if (isRoot)
			roots.append(fx);
	}
	return roots;
}

} // namespace

//==================================================================
//
// FxSchematicScene::SupportLinks
//
//==================================================================

void FxSchematicScene::SupportLinks::addBridgeLink(SchematicLink *link)
{
	if (link && !m_bridges.contains(link))
		m_bridges.push_back(link);
}

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

void FxSchematicScene::SupportLinks::addOutputLink(SchematicLink *link)
{
	if (link && !m_outputs.contains(link))
		m_outputs.push_back(link);
}

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

void FxSchematicScene::SupportLinks::addInputLink(SchematicLink *link)
{
	if (link && !m_inputs.contains(link))
		m_inputs.push_back(link);
}

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

void FxSchematicScene::SupportLinks::hideBridgeLinks()
{
	int i;
	for (i = 0; i < m_bridges.size(); i++)
		m_bridges[i]->hide();
}

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

void FxSchematicScene::SupportLinks::hideInputLinks()
{
	int i;
	for (i = 0; i < m_inputs.size(); i++)
		m_inputs[i]->hide();
}

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

void FxSchematicScene::SupportLinks::hideOutputLinks()
{
	int i;
	for (i = 0; i < m_outputs.size(); i++)
		m_outputs[i]->hide();
}

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

void FxSchematicScene::SupportLinks::showBridgeLinks()
{
	int i;
	for (i = 0; i < m_bridges.size(); i++)
		m_bridges[i]->show();
}

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

void FxSchematicScene::SupportLinks::showInputLinks()
{
	int i;
	for (i = 0; i < m_inputs.size(); i++)
		m_inputs[i]->show();
}

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

void FxSchematicScene::SupportLinks::showOutputLinks()
{
	int i;
	for (i = 0; i < m_outputs.size(); i++)
		m_outputs[i]->show();
}

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

void FxSchematicScene::SupportLinks::removeBridgeLinks(bool deleteLink)
{
	int i;
	for (i = 0; i < m_bridges.size(); i++) {
		SchematicLink *link = m_bridges[i];
		m_bridges.removeAt(i);
		if (deleteLink) {
			link->getStartPort()->removeLink(link);
			link->getEndPort()->removeLink(link);
			delete link;
		}
	}
}

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

void FxSchematicScene::SupportLinks::removeInputLinks(bool deleteLink)
{
	int i;
	for (i = 0; i < m_inputs.size(); i++) {
		SchematicLink *link = m_inputs[i];
		m_inputs.removeAt(i);
		if (deleteLink) {
			link->getStartPort()->removeLink(link);
			link->getEndPort()->removeLink(link);
			delete link;
		}
	}
}

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

void FxSchematicScene::SupportLinks::removeOutputLinks(bool deleteLink)
{
	int i;
	for (i = 0; i < m_outputs.size(); i++) {
		SchematicLink *link = m_outputs[i];
		m_outputs.removeAt(i);
		if (deleteLink) {
			link->getStartPort()->removeLink(link);
			link->getEndPort()->removeLink(link);
			delete link;
		}
	}
}

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

void FxSchematicScene::SupportLinks::clearAll()
{
	m_bridges.clear();
	m_inputs.clear();
	m_outputs.clear();
}

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

int FxSchematicScene::SupportLinks::size()
{
	return m_bridges.size() + m_inputs.size() + m_outputs.size();
}

//==================================================================
//
// FxSchematicScene
//
//==================================================================

FxSchematicScene::FxSchematicScene(QWidget *parent)
	: SchematicScene(parent), m_firstPoint(sceneRect().center()), m_xshHandle(0), m_fxHandle(0), m_addFxContextMenu(), m_disconnectionLinks(), m_connectionLinks(), m_isConnected(false), m_linkUnlinkSimulation(false), m_altPressed(false), m_lastPos(0, 0), m_currentFxNode(0), m_gridDimension(eSmall), m_isLargeScaled(true)
{
	m_selection = new FxSelection();
	m_selection->setFxSchematicScene(this);

	connect(m_selection, SIGNAL(doCollapse(const QList<TFxP> &)), this, SLOT(onCollapse(const QList<TFxP> &)));
	connect(m_selection, SIGNAL(doExplodeChild(const QList<TFxP> &)), this, SIGNAL(doExplodeChild(const QList<TFxP> &)));
	connect(this, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()));

	m_addFxContextMenu.setSelection(m_selection);
	m_highlightedLinks.clear();
}

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

FxSchematicScene::~FxSchematicScene()
{
	if (m_selection)
		delete m_selection;
}

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

void FxSchematicScene::setApplication(TApplication *app)
{
	m_app = app;

	m_xshHandle = app->getCurrentXsheet();
	m_fxHandle = app->getCurrentFx();
	m_frameHandle = app->getCurrentFrame();
	m_columnHandle = app->getCurrentColumn();

	if (m_fxHandle)
		connect(m_fxHandle, SIGNAL(fxSwitched()), this, SLOT(onCurrentFxSwitched()));

	m_addFxContextMenu.setApplication(app);

	m_selection->setXsheetHandle(m_xshHandle);
	m_selection->setFxHandle(m_fxHandle);
}

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

void FxSchematicScene::updateScene()
{
	if (!views().empty())
#if QT_VERSION >= 0x050000
		m_isLargeScaled = views().at(0)->matrix().determinant() >= 1.0;
#else
		m_isLargeScaled = views().at(0)->matrix().det() >= 1.0;
#endif
	m_disconnectionLinks.clearAll();
	m_connectionLinks.clearAll();
	m_selectionOldPos.clear();

	clearSelection();
	clearAllItems();

	m_table.clear();
	m_groupedTable.clear();
	m_groupEditorTable.clear();
	m_macroEditorTable.clear();

	m_currentFxNode = 0;

	// GroupId->Fx
	QMap<int, QList<TFxP>> groupedFxs;
	QMap<int, QList<SchematicNode *>> editedGroup;
	QMap<TMacroFx *, QList<SchematicNode *>> editedMacro;

	TXsheet *xsh = m_xshHandle->getXsheet();
	m_gridDimension = xsh->getFxDag()->getDagGridDimension();
	TFxSet *fxSet = xsh->getFxDag()->getInternalFxs();
	int i;

	FxDag *fxDag = xsh->getFxDag();
	//Add XSheetFX node
	addFxSchematicNode(fxDag->getXsheetFx());

	//Add outputFx nodes
	int k;
	for (k = 0; k < fxDag->getOutputFxCount(); k++) {
		TOutputFx *fx = fxDag->getOutputFx(k);
		if (fx->getAttributes()->isGrouped() && !fx->getAttributes()->isGroupEditing()) {
			groupedFxs[fx->getAttributes()->getGroupId()].push_back(fx);
			continue;
		}
		SchematicNode *node = addFxSchematicNode(fx);
		if (fx->getAttributes()->isGrouped())
			editedGroup[fx->getAttributes()->getEditingGroupId()].append(node);
	}

	//Add columnFx and zeraryFx nodes
	for (i = 0; i < xsh->getColumnCount(); i++) {
		TXshColumn *column = xsh->getColumn(i);
		TFx *fx = 0;
		if (TXshLevelColumn *lc = column->getLevelColumn())
			fx = lc->getLevelColumnFx();
		else if (TXshPaletteColumn *pc = dynamic_cast<TXshPaletteColumn *>(column))
			fx = pc->getPaletteColumnFx();
		else if (TXshZeraryFxColumn *zc = dynamic_cast<TXshZeraryFxColumn *>(column))
			fx = zc->getZeraryColumnFx();
		if (!fx)
			continue;

		if (fx->getAttributes()->isGrouped() && !fx->getAttributes()->isGroupEditing()) {
			groupedFxs[fx->getAttributes()->getGroupId()].push_back(fx);
			continue;
		}
		if (column->isEmpty() && fx && fx->getOutputConnectionCount() == 0)
			continue;
		SchematicNode *node = addFxSchematicNode(fx);
		if (fx->getAttributes()->isGrouped())
			editedGroup[fx->getAttributes()->getEditingGroupId()].append(node);
	}

	//Add normalFx
	for (i = 0; i < fxSet->getFxCount(); i++) {
		TFx *fx = fxSet->getFx(i);
		TMacroFx *macro = dynamic_cast<TMacroFx *>(fx);
		if (fx->getAttributes()->isGrouped() && !fx->getAttributes()->isGroupEditing()) {
			groupedFxs[fx->getAttributes()->getGroupId()].push_back(fx);
			continue;
		} else if (macro && macro->isEditing()) {
			vector<TFxP> fxs = macro->getFxs();
			int j;
			for (j = 0; j < (int)fxs.size(); j++) {
				SchematicNode *node = addFxSchematicNode(fxs[j].getPointer());
				editedMacro[macro].append(node);
				if (fxs[j]->getAttributes()->isGrouped() && macro->getAttributes()->isGroupEditing())
					editedGroup[fx->getAttributes()->getEditingGroupId()].append(node);
			}
			continue;
		}
		SchematicNode *node = addFxSchematicNode(fx);
		if (fx->getAttributes()->isGrouped())
			editedGroup[fx->getAttributes()->getEditingGroupId()].append(node);
	}

	//grouped node
	QMap<int, QList<TFxP>>::const_iterator it;
	for (it = groupedFxs.begin(); it != groupedFxs.end(); it++) {
		FxSchematicNode *node = addGroupedFxSchematicNode(it.key(), it.value());
		TFx *fx = node->getFx();
		assert(fx);
		int editingGroupId = fx->getAttributes()->getEditingGroupId();
		if (editingGroupId != -1)
			editedGroup[editingGroupId].append(node);
	}

	keepSubgroup(editedGroup);
	updateEditedGroups(editedGroup);
	updateEditedMacros(editedMacro);
	updateLink();
	m_nodesToPlace.clear();
}

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

void FxSchematicScene::updateEditedGroups(const QMap<int, QList<SchematicNode *>> &editedGroup)
{
	QMap<int, QList<SchematicNode *>>::const_iterator it;
	for (it = editedGroup.begin(); it != editedGroup.end(); it++) {
		int zValue = 2;
		QMap<int, QList<SchematicNode *>>::const_iterator it2 = editedGroup.begin();
		while (it2 != editedGroup.end()) {
			FxSchematicNode *placedFxNode = dynamic_cast<FxSchematicNode *>(it2.value()[0]);
			FxSchematicNode *fxNode = dynamic_cast<FxSchematicNode *>(it.value()[0]);
			if (!placedFxNode || !fxNode) {
				it2++;
				continue;
			}
			int placedGroupedId = placedFxNode->getFx()->getAttributes()->getEditingGroupId();
			if (fxNode->getFx()->getAttributes()->isContainedInGroup(placedGroupedId) &&
				fxNode->getFx()->getAttributes()->getEditingGroupId() != it2.key())
				zValue += 2;
			it2++;
		}
		FxSchematicGroupEditor *node = addEditedGroupedFxSchematicNode(it.key(), it.value());
		node->setZValue(zValue);
		node->setGroupedNodeZValue(zValue + 1);
	}
}

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

void FxSchematicScene::updateEditedMacros(const QMap<TMacroFx *, QList<SchematicNode *>> &editedMacro)
{
	QMap<TMacroFx *, QList<SchematicNode *>>::const_iterator it;
	for (it = editedMacro.begin(); it != editedMacro.end(); it++) {
		TMacroFx *macro = it.key();
		int zValue = 2;
		if (macro->getAttributes()->isGrouped()) {
			FxSchematicGroupEditor *containingGroup = m_groupEditorTable[macro->getAttributes()->getEditingGroupId()];
			assert(containingGroup);
			zValue = containingGroup->zValue() + 2;
		}
		FxSchematicMacroEditor *node = addEditedMacroFxSchematicNode(it.key(), it.value());
		node->setZValue(zValue);
		node->setGroupedNodeZValue(zValue + 1);
	}
}

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

FxSchematicNode *FxSchematicScene::addFxSchematicNode(TFx *fx)
{
	FxSchematicNode *node = createFxSchematicNode(fx);
	if (!node)
		return 0;
	connect(node, SIGNAL(sceneChanged()), this, SLOT(onSceneChanged()));
	connect(node, SIGNAL(xsheetChanged()), this, SLOT(onXsheetChanged()));
	connect(node, SIGNAL(switchCurrentFx(TFx *)), this, SLOT(onSwitchCurrentFx(TFx *)));
	connect(node, SIGNAL(currentColumnChanged(int)), this, SLOT(onCurrentColumnChanged(int)));

	connect(node, SIGNAL(fxNodeDoubleClicked()), this, SLOT(onFxNodeDoubleClicked()));
	if (fx->getAttributes()->getDagNodePos() == TConst::nowhere) {
		node->resize(m_gridDimension == 0);
		placeNode(node);
	} else
		updatePosition(node, fx->getAttributes()->getDagNodePos());
	m_table[fx] = node;
	return node;
}

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

FxSchematicNode *FxSchematicScene::addGroupedFxSchematicNode(int groupId, const QList<TFxP> &groupedFxs)
{
	TFxSet *terminals = getXsheet()->getFxDag()->getTerminalFxs();
	QList<TFxP> roots = getRoots(groupedFxs, terminals);
	if (roots.isEmpty())
		return 0;
	wstring name = roots[0]->getAttributes()->getGroupName(false);
	FxGroupNode *node = new FxGroupNode(this, groupedFxs, roots, groupId, name);
	if (!node)
		return 0;
	connect(node, SIGNAL(sceneChanged()), this, SLOT(onSceneChanged()));
	connect(node, SIGNAL(xsheetChanged()), this, SLOT(onXsheetChanged()));
	connect(node, SIGNAL(switchCurrentFx(TFx *)), this, SLOT(onSwitchCurrentFx(TFx *)));
	connect(node, SIGNAL(currentColumnChanged(int)), this, SLOT(onCurrentColumnChanged(int)));
	m_groupedTable[groupId] = node;
	return node;
}

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

FxSchematicGroupEditor *FxSchematicScene::addEditedGroupedFxSchematicNode(int groupId,
																		  const QList<SchematicNode *> &groupedFxs)
{
	FxSchematicGroupEditor *editorGroup = new FxSchematicGroupEditor(groupId, groupedFxs, this);
	m_groupEditorTable[groupId] = editorGroup;
	return editorGroup;
}

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

FxSchematicMacroEditor *FxSchematicScene::addEditedMacroFxSchematicNode(TMacroFx *macro, const QList<SchematicNode *> &groupedFxs)
{
	FxSchematicMacroEditor *editorMacro = new FxSchematicMacroEditor(macro, groupedFxs, this);
	m_macroEditorTable[macro] = editorMacro;
	return editorMacro;
}

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

void FxSchematicScene::updatePosition(FxSchematicNode *node, const TPointD &pos)
{
	node->setPos(QPointF(pos.x, pos.y));
	node->getFx()->getAttributes()->setDagNodePos(pos);
	QVector<SchematicNode *> placedNodes = getPlacedNode(node);
	int step = m_gridDimension == eLarge ? 100 : 50;
	TPointD offset(0, -step);
	int i;
	for (i = 0; i < placedNodes.size(); i++) {
		FxSchematicNode *placedNode = dynamic_cast<FxSchematicNode *>(placedNodes[i]);
		assert(placedNode);
		TPointD newPos = placedNode->getFx()->getAttributes()->getDagNodePos() + offset;
		updatePosition(placedNode, newPos);
	}
}

//------------------------------------------------------------------
/*! create node depends on the fx type
*/
FxSchematicNode *FxSchematicScene::createFxSchematicNode(TFx *fx)
{
	if (TLevelColumnFx *lcFx = dynamic_cast<TLevelColumnFx *>(fx))
		return new FxSchematicColumnNode(this, lcFx);
	else if (TPaletteColumnFx *pfx = dynamic_cast<TPaletteColumnFx *>(fx))
		return new FxSchematicPaletteNode(this, pfx);
	else if (TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(fx))
		return new FxSchematicZeraryNode(this, zfx);
	else if (TXsheetFx *xfx = dynamic_cast<TXsheetFx *>(fx))
		return new FxSchematicXSheetNode(this, xfx);
	else if (TOutputFx *ofx = dynamic_cast<TOutputFx *>(fx))
		return new FxSchematicOutputNode(this, ofx);
	else
		return new FxSchematicNormalFxNode(this, fx);
}

//------------------------------------------------------------------
/*! place nodes of which positions are not specified manually
*/
void FxSchematicScene::placeNode(FxSchematicNode *node)
{
	if (!node)
		return;
	int step = m_gridDimension == eLarge ? 100 : 50;
	FxDag *fxDag = m_xshHandle->getXsheet()->getFxDag();
	QRectF nodeRect = node->boundingRect();
	if (node->isA(eOutpuFx)) {
		//I'm placing an output node
		TFx *xsheetFx = fxDag->getXsheetFx();
		TFxPort *outPort = xsheetFx->getOutputConnection(0);
		TFx *connectedOutput = outPort ? outPort->getOwnerFx() : 0;
		if (connectedOutput && connectedOutput == node->getFx()) {
			//The output node is connected to the xsheet node
			TPointD pos = xsheetFx->getAttributes()->getDagNodePos();
			if (pos != TConst::nowhere)
				nodeRect.translate(pos.x + 120, pos.y);
			else
				nodeRect.translate(sceneRect().center());
			while (!isAnEmptyZone(nodeRect))
				nodeRect.translate(0, step);
		} else {
			//The output node is not connected to the xsheet node
			TFx *fx = node->getFx();
			TFxPort *port = fx->getInputPort(0);
			TFx *inputFx = port->getFx();
			if (inputFx) {
				//The output node is connected to another node
				TPointD pos = inputFx->getAttributes()->getDagNodePos();
				if (pos != TConst::nowhere)
					nodeRect.translate(pos.x + 120, pos.y);
				else {
					m_nodesToPlace[inputFx].append(node);
					return;
				}
			} else {
				//The output node is not connected
				QPointF pos = sceneRect().center();
				nodeRect.translate(pos);
			}
		}
		while (!isAnEmptyZone(nodeRect))
			nodeRect.translate(0, step);
		QPointF newPos = nodeRect.topLeft();
		node->getFx()->getAttributes()->setDagNodePos(TPointD(newPos.x(), newPos.y()));
		node->setPos(newPos);
		return;
	} else if (node->isA(eXSheetFx)) {
		//I'm placing the xsheet node
		TFxSet *terminalFxs = fxDag->getTerminalFxs();
		int i;
		double maxX = m_firstPoint.x();
		for (i = 0; i < terminalFxs->getFxCount(); i++) {
			TFx *terminalFx = terminalFxs->getFx(i);
			if (terminalFx->getAttributes()->getDagNodePos() == TConst::nowhere)
				continue;
			maxX = tmax(maxX, terminalFx->getAttributes()->getDagNodePos().x);
		}
		TPointD oldPos = node->getFx()->getAttributes()->getDagNodePos();
		QPointF pos;
		if (oldPos == TConst::nowhere)
			pos = QPointF(maxX + 120, m_firstPoint.y());
		else
			pos = QPointF(maxX + 120 > oldPos.x ? maxX + 120 : oldPos.x, oldPos.y);
		node->getFx()->getAttributes()->setDagNodePos(TPointD(pos.x(), pos.y()));
		node->setPos(pos);
		return;
	} else if (node->isA(eMacroFx) || node->isA(eNormalFx) ||
			   node->isA(eNormalLayerBlendingFx) || node->isA(eNormalMatteFx) || node->isA(eNormalImageAdjustFx)) {
		//I'm placing an effect or a macro
		TFx *inputFx = node->getFx()->getInputPort(0)->getFx();
		QPointF pos;
		if (inputFx) {
			if (inputFx->getAttributes()->getDagNodePos() != TConst::nowhere) {
				TPointD dagPos = inputFx->getAttributes()->getDagNodePos() + TPointD(150, 0);
				pos = QPointF(dagPos.x, dagPos.y);
				nodeRect.moveTopLeft(pos);
				while (!isAnEmptyZone(nodeRect))
					nodeRect.translate(0, -step);
				pos = nodeRect.topLeft();
			} else {
				m_nodesToPlace[inputFx].append(node);
				return;
			}
		} else {
			pos = sceneRect().center();
			nodeRect.moveTopLeft(pos);
			while (!isAnEmptyZone(nodeRect))
				nodeRect.translate(0, -step);
			pos = nodeRect.topLeft();
		}
		node->getFx()->getAttributes()->setDagNodePos(TPointD(pos.x(), pos.y()));
		node->setPos(QPointF(pos));
		if (m_nodesToPlace.contains(node->getFx())) {
			QList<FxSchematicNode *> nodes = m_nodesToPlace[node->getFx()];
			int i;
			for (i = 0; i < nodes.size(); i++)
				placeNode(nodes[i]);
		}
		return;
	} else if (node->isA(eZeraryFx) || node->isA(eColumnFx) || node->isA(eGroupedFx)) {
		//I'm placing a column
		nodeRect.translate(m_firstPoint);
		nodeRect.translate(10, 10);
		while (!isAnEmptyZone(nodeRect))
			nodeRect.translate(0, -step);
		QPointF newPos = nodeRect.topLeft();
		node->getFx()->getAttributes()->setDagNodePos(TPointD(newPos.x(), newPos.y()));
		node->setPos(QPointF(newPos));
		return;
	}
}

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

void FxSchematicScene::updateLink()
{
	TXsheet *xsh = m_xshHandle->getXsheet();

	// Iterate the fxs table
	QMap<TFx *, FxSchematicNode *>::iterator it;
	for (it = m_table.begin(); it != m_table.end(); ++it) {
		FxSchematicNode *node = it.value();
		if (!node)
			continue; // Should be asserted? Is it legal?

		TFx *fx = it.key();
		TFx *inputPortsFx = fx;

		if (TZeraryColumnFx *fx2 = dynamic_cast<TZeraryColumnFx *>(fx)) {
			inputPortsFx = fx2->getZeraryFx();
			if (!inputPortsFx)
				return; // Should really never happen. Should be asserted...
		}

		for (int i = 0; i != inputPortsFx->getInputPortCount(); ++i) {
			TFxPort *port = inputPortsFx->getInputPort(i);

			if (TFx *linkedFx = port->getFx()) {
				if (!linkedFx->getAttributes()->isGrouped() || linkedFx->getAttributes()->isGroupEditing()) {
					// Not in a group / open group case
					assert(m_table.contains(linkedFx));

					if (m_table.contains(linkedFx)) {
						FxSchematicNode *linkedNode = m_table[linkedFx];
						SchematicPort *p0 = linkedNode->getOutputPort();
						SchematicPort *p1 = node->getInputPort(i);
						if (p0 && p1)
							p0->makeLink(p1);
					}
				} else {
					assert(m_groupedTable.contains(linkedFx->getAttributes()->getGroupId()));

					if (m_groupedTable.contains(linkedFx->getAttributes()->getGroupId())) {
						FxSchematicNode *linkedNode = m_groupedTable[linkedFx->getAttributes()->getGroupId()];
						SchematicPort *p0 = linkedNode->getOutputPort();
						SchematicPort *p1 = node->getInputPort(i);
						if (p0 && p1)
							p0->makeLink(p1);
					}
				}
			}
		}
		if (xsh->getFxDag()->getTerminalFxs()->containsFx(fx)) {
			SchematicPort *p0 = node->getOutputPort();
			SchematicPort *p1 = m_table[xsh->getFxDag()->getXsheetFx()]->getInputPort(0);
			p0->makeLink(p1);
		}
	}
	QMap<int, FxGroupNode *>::iterator it2;
	for (it2 = m_groupedTable.begin(); it2 != m_groupedTable.end(); it2++) {
		FxGroupNode *node = it2.value();
		if (!node)
			continue;
		int i, fxCount = node->getFxCount();
		bool xsheetConnected = false;
		for (i = 0; i < fxCount; i++) {
			TFx *fx = node->getFx(i);
			if (xsh->getFxDag()->getTerminalFxs()->containsFx(fx) && !xsheetConnected) {
				SchematicPort *p0 = node->getOutputPort();
				SchematicPort *p1 = m_table[xsh->getFxDag()->getXsheetFx()]->getInputPort(0);
				p0->makeLink(p1);
				xsheetConnected = true;
			}

			TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(fx);
			if (zfx)
				fx = zfx->getZeraryFx();
			if (fx) {
				int j;
				for (j = 0; j < fx->getInputPortCount(); j++) {
					TFx *linkedFx = fx->getInputPort(j)->getFx();
					if (!linkedFx)
						continue;
					if (!linkedFx->getAttributes()->isGrouped() || linkedFx->getAttributes()->isGroupEditing()) {
						assert(m_table.contains(linkedFx));
						if (m_table.contains(linkedFx)) {
							FxSchematicNode *linkedNode = m_table[linkedFx];
							SchematicPort *p0 = linkedNode->getOutputPort();
							SchematicPort *p1 = node->getInputPort(0);
							if (p0 && p1)
								p0->makeLink(p1);
						}
					} else {
						int linkedGroupId = linkedFx->getAttributes()->getGroupId();
						assert(m_groupedTable.contains(linkedGroupId));
						if (m_groupedTable.contains(linkedGroupId)) {
							FxGroupNode *linkedNode = m_groupedTable[linkedGroupId];
							if (linkedNode == node)
								continue;
							SchematicPort *p0 = linkedNode->getOutputPort();
							SchematicPort *p1 = node->getInputPort(0);
							if (p0 && p1 && !p0->isLinkedTo(p1))
								p0->makeLink(p1);
						}
					}
				}
			}
		}
	}

	//to solve an edit macro problem: create a dummy link
	QMap<TMacroFx *, FxSchematicMacroEditor *>::iterator it3;
	for (it3 = m_macroEditorTable.begin(); it3 != m_macroEditorTable.end(); it3++) {
		TMacroFx *macro = it3.key();
		int i;
		FxSchematicNode *root = m_table[macro->getRoot()];
		SchematicPort *p0 = root->getOutputPort();
		for (i = 0; i < macro->getOutputConnectionCount(); i++) {
			TFxPort *outConnection = macro->getOutputConnection(i);
			TFx *outFx = outConnection->getOwnerFx();
			TMacroFx *outMacroFx = dynamic_cast<TMacroFx *>(outFx);
			if (outMacroFx && outMacroFx->isEditing()) {
				vector<TFxP> fxs = outMacroFx->getFxs();
				int k;
				for (k = 0; k < (int)fxs.size(); k++) {
					TFx *fx = fxs[k].getPointer();
					int j;
					for (j = 0; j < fx->getInputPortCount(); j++)
						if (outConnection == fx->getInputPort(j)) {
							outFx = fx;
							break;
						}
					if (outFx != outMacroFx)
						break;
				}
			}

			int j;
			for (j = 0; j < outFx->getInputPortCount(); j++)
				if (outFx->getInputPort(j)->getFx() == macro) {
					SchematicPort *p1 = m_table[outFx]->getInputPort(j);
					p0->makeLink(p1);
					break;
				}
		}
		if (xsh->getFxDag()->getTerminalFxs()->containsFx(macro)) {
			assert(root);
			if (!root)
				continue;
			SchematicPort *p1 = m_table[xsh->getFxDag()->getXsheetFx()]->getInputPort(0);
			p0->makeLink(p1);
		}
	}
	updateDuplcatedNodesLink();
}

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

void FxSchematicScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *cme)
{
	QPointF scenePos = cme->scenePos();
	QList<QGraphicsItem *> itemList = items(scenePos);
	if (!itemList.isEmpty()) {
		SchematicScene::contextMenuEvent(cme);
		return;
	}

	QMenu menu(views()[0]);

	if (cme->modifiers() & Qt::ControlModifier) {
		menu.addAction(m_addFxContextMenu.getAgainCommand(AddFxContextMenu::Add));
		if (!menu.actions().isEmpty()) {
			menu.exec(cme->screenPos());
			return;
		}
	}

	QAction *addOutputFx = CommandManager::instance()->getAction("MI_NewOutputFx");
	QAction *copy = CommandManager::instance()->getAction("MI_Copy");
	QAction *cut = CommandManager::instance()->getAction("MI_Cut");
	QAction *paste = CommandManager::instance()->getAction("MI_Paste");
	QIcon addOutputFxIcon = createQIconOnOffPNG("output", false);
	if (addOutputFx)
		addOutputFx->setIcon(addOutputFxIcon);

	m_addFxContextMenu.setCurrentCursorScenePos(cme->scenePos());

	menu.addMenu(m_addFxContextMenu.getAddMenu());
	if (addOutputFx)
		menu.addAction(addOutputFx);
	menu.addSeparator();
	menu.addAction(copy);
	menu.addAction(cut);
	menu.addAction(paste);
	m_selection->setPastePosition(TPointD(scenePos.x(), scenePos.y()));
	menu.exec(cme->screenPos());
	m_selection->setPastePosition(TConst::nowhere);
}

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

QPointF FxSchematicScene::nearestPoint(const QPointF &point)
{
	QRectF rect(0, 0, 0.1, 0.1);
	rect.moveCenter(point);
	QList<QGraphicsItem *> itemList = items(rect);
	while (itemList.isEmpty()) {
		rect.adjust(-0.1, -0.1, 0.1, 0.1);
		itemList = items(rect);
	}
#if QT_VERSION >= 0x050000
	/*
	FIXME: QTransform() のデフォルトは Qt4.8 の itemAt() と比べて equivant だろうか?
   */
	QGraphicsItem *item = itemAt(rect.bottomLeft(), QTransform());
	if (item)
		return rect.bottomLeft();
	item = itemAt(rect.bottomRight(), QTransform());
	if (item)
		return rect.bottomRight();
	item = itemAt(rect.topLeft(), QTransform());
	if (item)
		return rect.topLeft();
	item = itemAt(rect.topRight(), QTransform());
#else
	QGraphicsItem *item = itemAt(rect.bottomLeft());
	if (item)
		return rect.bottomLeft();
	item = itemAt(rect.bottomRight());
	if (item)
		return rect.bottomRight();
	item = itemAt(rect.topLeft());
	if (item)
		return rect.topLeft();
	item = itemAt(rect.topRight());
#endif
	if (item)
		return rect.topRight();
	return QPointF();
}

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

FxSchematicNode *FxSchematicScene::getFxNodeFromPosition(const QPointF &pos)
{
	QList<QGraphicsItem *> pickedItems = items(pos);
	int i = 0;
	for (i; i < pickedItems.size(); i++) {
		FxSchematicNode *fxNode = dynamic_cast<FxSchematicNode *>(pickedItems.at(i));
		if (fxNode)
			return fxNode;
		FxSchematicPort *fxPort = dynamic_cast<FxSchematicPort *>(pickedItems.at(i));
		if (fxPort)
			return fxPort->getDock()->getNode();
	}
	return 0;
}

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

void FxSchematicScene::updateDuplcatedNodesLink()
{
	QMap<TFx *, FxSchematicNode *>::iterator it;

	// fx2node contains only duplicated nodes
	// and zerary duplicated node s
	QMap<TFx *, FxSchematicNode *> fx2node;
	for (it = m_table.begin(); it != m_table.end(); ++it) {
		TFx *fx = it.key();
		FxSchematicNode *node = it.value();
		if (TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx)) {
			fx = zcfx->getZeraryFx();
			if (!fx)
				return;
		}
		assert(fx2node.count(fx) == 0);
		if (fx->getLinkedFx() == fx)
			continue;
		fx2node[fx] = node;
	}

	// trovo i link
	std::set<TFx *> visited;
	for (it = fx2node.begin(); it != fx2node.end(); ++it) {
		TFx *fx = it.key();
		FxSchematicNode *node = it.value();
		assert(fx->getLinkedFx() != fx);
		if (visited.count(fx) > 0)
			continue;
		visited.insert(fx);
		FxSchematicNode *lastNode = node;
		assert(lastNode);
		FxSchematicPort *lastPort = lastNode->getLinkPort();
		assert(lastPort);
		for (fx = fx->getLinkedFx(); fx != it.key(); fx = fx->getLinkedFx()) {
			assert(visited.count(fx) == 0);
			if (visited.count(fx) > 0)
				break;
			visited.insert(fx);
			QMap<TFx *, FxSchematicNode *>::iterator h;
			h = fx2node.find(fx);
			if (h == fx2node.end())
				continue;

			assert(h != fx2node.end());
			FxSchematicNode *node = h.value();
			assert(node);
			FxSchematicPort *port = node->getLinkPort();
			assert(port);
			if (port && lastPort)
				port->makeLink(lastPort);
			lastNode = node;
			lastPort = port;
		}
	}
}

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

QGraphicsItem *FxSchematicScene::getCurrentNode()
{
	QList<QGraphicsItem *> allItems = items();

	QList<QGraphicsItem *>::iterator it = allItems.begin();
	for (it; it != allItems.end(); it++) {
		FxSchematicNode *node = dynamic_cast<FxSchematicNode *>(*it);
		if (node && node->getFx() == m_fxHandle->getFx())
			return node;
	}
	return 0;
}

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

void FxSchematicScene::onSelectionSwitched(TSelection *oldSel, TSelection *newSel)
{
	if (m_selection == oldSel && m_selection != newSel)
		clearSelection();
}

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

void FxSchematicScene::onSelectionChanged()
{
	m_selection->selectNone();
	int i, size = m_highlightedLinks.size();
	for (i = 0; i < size; i++) {
		SchematicLink *link = m_highlightedLinks[i];
		link->setHighlighted(false);
		link->update();
	}
	m_highlightedLinks.clear();
	QList<QGraphicsItem *> slcItems = selectedItems();
	QList<QGraphicsItem *>::iterator it;
	for (it = slcItems.begin(); it != slcItems.end(); it++) {
		FxSchematicNode *node = dynamic_cast<FxSchematicNode *>(*it);
		if (node) {
			if (!node->isA(eGroupedFx)) {
				if (node->isA(eXSheetFx))
					continue;
				m_selection->select(node->getFx());
				if (node->isA(eColumnFx)) {
					FxSchematicColumnNode *columnNode = dynamic_cast<FxSchematicColumnNode *>(node);
					if (columnNode)
						m_selection->select(columnNode->getColumnIndex());
					else {
						FxSchematicPaletteNode *paletteNode = dynamic_cast<FxSchematicPaletteNode *>(node);
						if (paletteNode)
							m_selection->select(paletteNode->getColumnIndex());
					}
				}
			} else {
				FxGroupNode *groupNode = dynamic_cast<FxGroupNode *>(node);
				assert(groupNode);
				QList<TFxP> fxs = groupNode->getGroupedFxs();
				for (i = 0; i < fxs.size(); i++) {
					m_selection->select(fxs[i].getPointer());
					TLevelColumnFx *colFx = dynamic_cast<TLevelColumnFx *>(fxs[i].getPointer());
					if (colFx) {
						if (TXshLevelColumn *column = colFx->getColumn()) {
							int colIndex = column->getIndex();
							m_selection->select(colIndex);
						}
					}
				}
			}
			highlightLinks(node, true);
		}
		SchematicLink *link = dynamic_cast<SchematicLink *>(*it);
		if (link)
			m_selection->select(link);
	}
	m_selection->makeCurrent();
	TSelectionHandle *selHandle = TSelectionHandle::getCurrent();
	selHandle->notifySelectionChanged();
}

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

void FxSchematicScene::reorderScene()
{
	int step = m_gridDimension == eLarge ? 100 : 50;
	m_placedFxs.clear();
	QPointF sceneCenter = sceneRect().center();
	double minY = sceneCenter.y();
	double maxX = sceneCenter.x();
	double y = minY;
	double x = maxX;

	TXsheet *xsh = m_xshHandle->getXsheet();
	int i = 0;
	for (i = 0; i < xsh->getColumnCount(); i++) {
		TXshColumn *column = xsh->getColumn(i);
		TFx *fx = column->getFx();

		if (column->isEmpty() || !fx)
			continue;

		TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(fx);
		if (zfx && (zfx->getZeraryFx()->getInputPortCount() > 0)) {
			TFxPort *port = zfx->getZeraryFx()->getInputPort(0);
			if (port && port->getFx())
				continue;
		}

		if (zfx && m_placedFxs.contains(zfx->getZeraryFx()))
			continue;

		x = sceneCenter.x();
		placeNodeAndParents(fx, x, maxX, minY);
		y -= step;
		minY = tmin(y, minY);
	}

	//remove retrolink
	for (i = 0; i < xsh->getColumnCount(); i++) {
		TXshColumn *column = xsh->getColumn(i);
		TFx *fx = column->getFx();
		if (column->isEmpty() || !fx)
			continue;

		TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(fx);
		if (zfx && m_placedFxs.contains(zfx->getZeraryFx()))
			continue;

		if (zfx && (zfx->getZeraryFx()->getInputPortCount() > 0)) {
			TFxPort *port = zfx->getZeraryFx()->getInputPort(0);
			if (port && port->getFx())
				continue;
		}

		for (int j = 0; j < fx->getOutputConnectionCount(); j++) {
			TFx *outFx = fx->getOutputConnection(j)->getOwnerFx();
			removeRetroLinks(outFx, maxX);
		}
	}

	double middleY = (sceneCenter.y() + minY + step) * 0.5;
	placeNodeAndParents(xsh->getFxDag()->getXsheetFx(), maxX, maxX, middleY);

	FxDag *fxDag = xsh->getFxDag();
	TFxSet *fxSet = fxDag->getInternalFxs();
	for (i = 0; i < fxSet->getFxCount(); i++) {
		TFx *fx = fxSet->getFx(i);
		if (m_placedFxs.contains(fx))
			continue;

		fx->getAttributes()->setDagNodePos(TPointD(sceneCenter.x() + 120, minY));
		minY -= step;
	}
	updateScene();
}

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

void FxSchematicScene::removeRetroLinks(TFx *fx, double &maxX)
{
	if (!fx)
		return;
	for (int i = 0; i < fx->getInputPortCount(); i++) {
		TFx *inFx = fx->getInputPort(i)->getFx();
		if (!inFx)
			continue;
		TPointD inFxPos = inFx->getAttributes()->getDagNodePos();
		TPointD fxPos = fx->getAttributes()->getDagNodePos();
		if (fxPos.x <= inFxPos.x) {
			while (fxPos.x <= inFxPos.x)
				fxPos.x += 150;
			maxX = tmax(fxPos.x + 150, maxX);
			fx->getAttributes()->setDagNodePos(fxPos);
			for (int j = 0; j < fx->getOutputConnectionCount(); j++) {
				TFx *outFx = fx->getOutputConnection(j)->getOwnerFx();
				removeRetroLinks(outFx, maxX);
			}
		}
	}
}

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

void FxSchematicScene::placeNodeAndParents(TFx *fx, double x, double &maxX, double &minY)
{
	int step = m_gridDimension == eLarge ? 100 : 50;
	if (!fx)
		return;
	m_placedFxs.append(fx);
	if (fx->getFxType() == "STD_particlesFx" ||
		fx->getFxType() == "STD_Iwa_ParticlesFx") {
		TXsheet *xsh = m_xshHandle->getXsheet();
		int i = 0;
		for (i = 0; i < xsh->getColumnCount(); i++) {
			TFx *columnFx = xsh->getColumn(i)->getFx();
			TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(columnFx);
			if (zfx && zfx->getZeraryFx() == fx) {
				fx = zfx;
				break;
			}
		}
	}
	double y = minY;
	fx->getAttributes()->setDagNodePos(TPointD(x, y));
	if (fx->getOutputConnectionCount() == 0)
		minY -= step;
	x += 120;
	maxX = tmax(maxX, x);
	int i;
	for (i = 0; i < fx->getOutputConnectionCount(); i++) {
		TFx *outputFx = fx->getOutputConnection(i)->getOwnerFx();
		//controllo se e' una porta sorgente
		TFxPort *port = outputFx->getInputPort(0);
		if (port && port->getFx() != fx)
			continue;
		if (!m_placedFxs.contains(outputFx) || outputFx->getAttributes()->getDagNodePos().x < x) {
			placeNodeAndParents(outputFx, x, maxX, minY);
			y -= step;
			minY = tmin(y, minY);
		}
	}
}

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

void FxSchematicScene::onDisconnectFromXSheet()
{
	std::list<TFxP, std::allocator<TFxP>> list = m_selection->getFxs().toStdList();
	TFxCommand::disconnectNodesFromXsheet(list, m_xshHandle);
}

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

void FxSchematicScene::onConnectToXSheet()
{
	std::list<TFxP, std::allocator<TFxP>> list = m_selection->getFxs().toStdList();
	TFxCommand::connectNodesToXsheet(list, m_xshHandle);
}

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

void FxSchematicScene::onDeleteFx()
{
	std::list<TFxP, std::allocator<TFxP>> fxList = m_selection->getFxs().toStdList();
	std::list<TFxCommand::Link> linkList = m_selection->getLinks().toStdList();
	std::list<int> columnIndexList = m_selection->getColumnIndexes().toStdList();
	TFxCommand::deleteSelection(fxList, linkList, columnIndexList, m_xshHandle, m_fxHandle);
}

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

void FxSchematicScene::onDuplicateFx()
{
	QList<TFxP> fxs = m_selection->getFxs();
	if (fxs.empty())
		return;

	TUndoManager::manager()->beginBlock();

	int i, size = fxs.size();
	for (i = 0; i != size; ++i)
		TFxCommand::duplicateFx(fxs[i].getPointer(), m_xshHandle, m_fxHandle);

	TUndoManager::manager()->endBlock();
}

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

void FxSchematicScene::onUnlinkFx()
{
	QList<TFxP> fxs = m_selection->getFxs();
	if (fxs.empty())
		return;

	TUndoManager::manager()->beginBlock();

	int i, size = fxs.size();
	for (i = 0; i != size; ++i)
		TFxCommand::unlinkFx(fxs[i].getPointer(), m_fxHandle, m_xshHandle);

	TUndoManager::manager()->endBlock();
}

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

void FxSchematicScene::onMacroFx()
{
	TFxCommand::makeMacroFx(m_selection->getFxs().toVector().toStdVector(), m_app);
}

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

void FxSchematicScene::onExplodeMacroFx()
{
	if (TMacroFx *macroFx = dynamic_cast<TMacroFx *>(m_fxHandle->getFx()))
		TFxCommand::explodeMacroFx(macroFx, m_app);
}

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

void FxSchematicScene::onOpenMacroFx()
{
	if (TMacroFx *macroFx = dynamic_cast<TMacroFx *>(m_fxHandle->getFx())) {
		macroFx->editMacro(true);
		updateScene();
	}
}

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

void FxSchematicScene::onSavePresetFx()
{
	CommandManager::instance()->getAction("MI_SavePreset")->trigger();
}

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

void FxSchematicScene::onRemoveOutput()
{
	TFxCommand::removeOutputFx(m_fxHandle->getFx(), m_xshHandle, m_fxHandle);
}

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

void FxSchematicScene::onActivateOutput()
{
	TFxCommand::makeOutputFxCurrent(m_fxHandle->getFx(), m_xshHandle);
}

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

void FxSchematicScene::onPreview()
{
	emit showPreview(m_fxHandle->getFx());
}

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

void FxSchematicScene::onCacheFx()
{
	setEnableCache(true);
}

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

void FxSchematicScene::onUncacheFx()
{
	setEnableCache(false);
}

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

void FxSchematicScene::setEnableCache(bool toggle)
{
	QList<TFxP> selectedFxs = m_selection->getFxs();
	int i;
	for (i = 0; i < selectedFxs.size(); i++) {
		TFx *fx = selectedFxs[i].getPointer();
		TZeraryColumnFx *zcfx = dynamic_cast<TZeraryColumnFx *>(fx);
		if (zcfx)
			fx = zcfx->getZeraryFx();
		TFxAttributes *attr = fx->getAttributes();
		if (!attr->isGrouped() || attr->isGroupEditing())
			if (toggle)
				TPassiveCacheManager::instance()->enableCache(fx);
			else
				TPassiveCacheManager::instance()->disableCache(fx);
		else {
			QMap<int, FxGroupNode *>::iterator it;
			for (it = m_groupedTable.begin(); it != m_groupedTable.end(); it++) {
				FxGroupNode *group = it.value();
				QList<TFxP> roots = group->getRootFxs();
				int j;
				for (j = 0; j < roots.size(); j++)
					if (fx == roots[j].getPointer())
						if (toggle)
							TPassiveCacheManager::instance()->enableCache(fx);
						else
							TPassiveCacheManager::instance()->disableCache(fx);
				group->update();
			}
		}
	}
}

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

void FxSchematicScene::onCollapse(const QList<TFxP> &fxs)
{
	emit doCollapse(fxs);
}

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

void FxSchematicScene::onOpenSubxsheet()
{
	CommandManager *cm = CommandManager::instance();
	cm->execute("MI_OpenChild");
}

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

TXsheet *FxSchematicScene::getXsheet()
{
	return m_xshHandle->getXsheet();
}

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

void FxSchematicScene::onXsheetChanged()
{
	m_xshHandle->notifyXsheetChanged();
}

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

void FxSchematicScene::onSceneChanged()
{
	m_app->getCurrentScene()->notifySceneChanged();
}

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

void FxSchematicScene::onSwitchCurrentFx(TFx *fx)
{
	if (m_fxHandle->getFx() == fx)
		return;
	if (fx) {
		// Forbid update of the swatch upon column switch. This could generate
		// a further useless render...
		SwatchViewer::suspendRendering(true, false);

		int columnIndex = fx->getReferenceColumnIndex();
		if (columnIndex >= 0) {
			m_columnHandle->setColumnIndex(columnIndex);
			m_app->getCurrentObject()->setObjectId(TStageObjectId::ColumnId(columnIndex));
		}

		SwatchViewer::suspendRendering(false);

		m_fxHandle->setFx(fx, false); //Setting the fx updates the swatch

		emit editObject();
	} else {
		m_fxHandle->setFx(0, false);
	}
}

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

void FxSchematicScene::onFxNodeDoubleClicked()
{
	//emitting fxSettingsShouldBeSwitched
	m_fxHandle->onFxNodeDoubleClicked();
}

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

void FxSchematicScene::onCurrentFxSwitched()
{
	if (m_currentFxNode)
		m_currentFxNode->setIsCurrentFxLinked(false, 0);
	if (m_table.contains(m_fxHandle->getFx())) {
		m_currentFxNode = m_table[m_fxHandle->getFx()];
		m_currentFxNode->setIsCurrentFxLinked(true, 0);
	} else
		m_currentFxNode = 0;
}

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

void FxSchematicScene::onCurrentColumnChanged(int index)
{
	m_app->getCurrentColumn()->setColumnIndex(index);
	m_app->getCurrentObject()->setObjectId(TStageObjectId::ColumnId(index));
}

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

TFx *FxSchematicScene::getCurrentFx()
{
	return m_fxHandle->getFx();
}

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

void FxSchematicScene::mousePressEvent(QGraphicsSceneMouseEvent *me)
{
	// avoid clear of selection by middle-click
	if (me->button() != Qt::MidButton)
		SchematicScene::mousePressEvent(me);

	if (me->button() == Qt::RightButton) {
		QList<QGraphicsItem *> pointedItems = items(me->scenePos());
		for (int i = 0; i < pointedItems.size(); i++) {
			FxSchematicNode *sn = dynamic_cast<FxSchematicNode *>(pointedItems[i]);
			if (sn) {
				m_fxHandle->setFx(sn->getFx(), false);
				if (!m_selection->isSelected(sn->getFx()))
					m_selection->select(sn->getFx());
				m_selection->makeCurrent();
			}
		}
	}

	QList<QGraphicsItem *> items = selectedItems();
#if QT_VERSION >= 0x050000
	QGraphicsItem *item = itemAt(me->scenePos(), QTransform());
#else
	QGraphicsItem *item = itemAt(me->scenePos());
#endif
	FxSchematicPort *port = dynamic_cast<FxSchematicPort *>(item);
	FxSchematicLink *link = dynamic_cast<FxSchematicLink *>(item);

	if (me->button() == Qt::MidButton) {
		int i;
		for (i = 0; i < items.size(); i++)
			items[i]->setSelected(true);
	}

	/*
  ここに入ったとき、
   ① 上記のSchematicScene::mousePressEvent(me)が呼ばれ
   ② 最上段にあるSchematicNode::mousePressEvent内でsetSelected(this)が呼ばれ
   ③ シグナルQGraphicsScene::selectionChanged()がエミットされ
   ④ スロットFxSchematticScene::onSelectionChanged()が呼ばれ
    その中で、いったんselectNone()され、その後選択ノードが追加される。
   ここにいたるまでにm_selectionが正しく更新される保障が無い。
   よって、選択Fx無しと勘違いして、FxSettingsが更新されないおそれがある。
   そこで、m_selection->isEmpty()では無く、QGraphicsScene::selectedItems()の個数で
   直接判断することにする
  */
	if (selectedItems().isEmpty()) {
		if (me->button() != Qt::MidButton && !item)
			m_fxHandle->setFx(0, false);
		return;
	}
	m_isConnected = false;
	if (!canDisconnectSelection(m_selection))
		return;
	m_selectionOldPos.clear();
	QList<TFxP> selectedFxs = m_selection->getFxs();
	int i;
	for (i = 0; i < selectedFxs.size(); i++) {
		TFxP selectedFx = selectedFxs[i];
		TPointD pos = selectedFx->getAttributes()->getDagNodePos();
		m_selectionOldPos.append(QPair<TFxP, TPointD>(selectedFx, pos));
	}
	FxsData fxsData;
	fxsData.setFxs(m_selection->getFxs(), m_selection->getLinks(),
				   m_selection->getColumnIndexes(), m_xshHandle->getXsheet());
	if (fxsData.isConnected() && me->button() == Qt::LeftButton && !port && !link)
		m_isConnected = true;
}

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

void FxSchematicScene::mouseMoveEvent(QGraphicsSceneMouseEvent *me)
{
	SchematicScene::mouseMoveEvent(me);

	m_lastPos = me->scenePos();

	bool leftButton = (QApplication::mouseButtons() == Qt::LeftButton);
	bool altButton = (QApplication::keyboardModifiers() == Qt::AltModifier);

	if (leftButton && m_isConnected && altButton) {
		m_linkUnlinkSimulation = true;

		simulateDisconnectSelection(true);
		m_connectionLinks.showBridgeLinks();

#if QT_VERSION >= 0x050000
		SchematicLink *link = dynamic_cast<SchematicLink *>(itemAt(m_lastPos, QTransform()));
#else
		SchematicLink *link = dynamic_cast<SchematicLink *>(itemAt(m_lastPos));
#endif
		if (link && (link->getEndPort() && link->getStartPort())) {
			TFxCommand::Link fxLink = m_selection->getBoundingFxs(link);
			if (fxLink == TFxCommand::Link())
				return;

			TFx *inputFx = fxLink.m_inputFx.getPointer();
			TFx *outputFx = fxLink.m_outputFx.getPointer();

			TFxSet *internalFxs = getXsheet()->getFxDag()->getInternalFxs();
			if (!internalFxs->containsFx(inputFx) && !dynamic_cast<TColumnFx *>(inputFx) &&
				!dynamic_cast<TXsheetFx *>(inputFx) && !dynamic_cast<TOutputFx *>(inputFx))
				return;
			if (!internalFxs->containsFx(outputFx) && !dynamic_cast<TColumnFx *>(outputFx) &&
				!dynamic_cast<TXsheetFx *>(outputFx) && !dynamic_cast<TOutputFx *>(outputFx))
				return;
		}

		m_connectionLinks.hideBridgeLinks();
		simulateInsertSelection(link, altButton && !!link);
	}
}

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

void FxSchematicScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *me)
{
	SchematicScene::mouseReleaseEvent(me);

	m_linkUnlinkSimulation = false;

	if ((m_disconnectionLinks.size() == 0) && (m_connectionLinks.size() == 0))
		return;

	TUndoManager::manager()->beginBlock();

	bool altButton = QApplication::keyboardModifiers() == Qt::AltModifier;
	if (altButton && m_isConnected) {
		if (m_connectionLinks.size() > 0) {
			const QList<SchematicLink *> &bridgeLinks = m_connectionLinks.getBridgeLinks();
			assert(bridgeLinks.size() <= 1);

			SchematicLink *link = bridgeLinks[0];

			if (link) {
				FxSchematicNode *outputNode = dynamic_cast<FxSchematicNode *>(link->getEndPort()->getNode());
				FxSchematicNode *inputNode = dynamic_cast<FxSchematicNode *>(link->getStartPort()->getNode());

				if (inputNode && outputNode) {
					SchematicPort *port = link->getStartPort();
					if (port->getType() == 200 || port->getType() == 204)
						port = link->getOtherPort(port);

					int i;
					for (i = 0; i < outputNode->getInputPortCount(); i++)
						if (port == outputNode->getInputPort(i))
							break;

					TFxCommand::Link fxLink;
					fxLink.m_outputFx = outputNode->getFx();
					fxLink.m_inputFx = inputNode->getFx();
					if (!outputNode->isA(eXSheetFx))
						fxLink.m_index = i;

					TFxCommand::connectFxs(fxLink, m_selection->getFxs().toStdList(), m_xshHandle, m_selectionOldPos);
				}
			}
		} else if (m_disconnectionLinks.size() > 0) {
			QList<TFxP> fxs = m_selection->getFxs();
			TFxCommand::disconnectFxs(fxs.toStdList(), m_xshHandle, m_selectionOldPos);
			m_selectionOldPos.clear();
		}
	}

	TUndoManager::manager()->endBlock();

	m_isConnected = false;
}

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

bool FxSchematicScene::event(QEvent *e)
{
	bool ret = SchematicScene::event(e);
	bool altPressed = QApplication::keyboardModifiers() == Qt::AltModifier;
	if (m_linkUnlinkSimulation && m_altPressed != altPressed)
		onAltModifierChanged(altPressed);
	m_altPressed = altPressed;
	return ret;
}

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

void FxSchematicScene::onInsertPaste()
{
	if (!m_selection->insertPasteSelection())
		DVGui::error(tr("Cannot Paste Insert a selection of unconnected FX nodes.\nSelect FX nodes and related links before copying or cutting the selection you want to paste."));
}

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

void FxSchematicScene::onAddPaste()
{
	if (!m_selection->addPasteSelection())
		DVGui::error(tr("Cannot Paste Add a selection of unconnected FX nodes.\nSelect FX nodes and related links before copying or cutting the selection you want to paste."));
}

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

void FxSchematicScene::onReplacePaste()
{
	if (!m_selection->replacePasteSelection())
		DVGui::error(tr("Cannot Paste Replace a selection of unconnected FX nodes.\nSelect FX nodes and related links before copying or cutting the selection you want to paste."));
}

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

void FxSchematicScene::onAltModifierChanged(bool altPressed)
{
	if (altPressed) {
		if (m_disconnectionLinks.size() == 0 && m_linkUnlinkSimulation)
			simulateDisconnectSelection(altPressed);
		if (m_connectionLinks.size() == 0 && m_linkUnlinkSimulation) {
			m_connectionLinks.showBridgeLinks();
#if QT_VERSION >= 0x050000
			SchematicLink *link = dynamic_cast<SchematicLink *>(itemAt(m_lastPos, QTransform()));
#else
			SchematicLink *link = dynamic_cast<SchematicLink *>(itemAt(m_lastPos));
#endif
			if (link && (!link->getEndPort() || !link->getStartPort()))
				return;
			m_connectionLinks.hideBridgeLinks();
			simulateInsertSelection(link, altPressed && !!link);
		}
	} else {
		if (m_disconnectionLinks.size() > 0 && m_linkUnlinkSimulation)
			simulateDisconnectSelection(altPressed);
		if (m_connectionLinks.size() > 0 && m_linkUnlinkSimulation) {
			m_connectionLinks.showBridgeLinks();
#if QT_VERSION >= 0x050000
			SchematicLink *link = dynamic_cast<SchematicLink *>(itemAt(m_lastPos, QTransform()));
#else
			SchematicLink *link = dynamic_cast<SchematicLink *>(itemAt(m_lastPos));
#endif
			if (link && (!link->getEndPort() || !link->getStartPort()))
				return;
			m_connectionLinks.hideBridgeLinks();
			simulateInsertSelection(link, altPressed && !!link);
		}
	}
}

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

void FxSchematicScene::onEditGroup()
{
	if (m_selection->isEmpty())
		return;
	QList<TFxP> fxs = m_selection->getFxs();
	int i;
	for (i = 0; i < fxs.size(); i++) {
		if (fxs[i]->getAttributes()->isGrouped() && !fxs[i]->getAttributes()->isGroupEditing()) {
			fxs[i]->getAttributes()->editGroup();
			TMacroFx *macro = dynamic_cast<TMacroFx *>(fxs[i].getPointer());
			if (macro) {
				vector<TFxP> macroFxs = macro->getFxs();
				int j;
				for (j = 0; j < (int)macroFxs.size(); j++)
					macroFxs[j]->getAttributes()->editGroup();
			}
		}
	}
	updateScene();
}

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

void FxSchematicScene::highlightLinks(FxSchematicNode *node, bool value)
{
	int i, portCount = node->getInputPortCount();
	//SchematicLink* ghostLink = m_supportLinks.getDisconnectionLink(eGhost);
	for (i = 0; i < portCount; i++) {
		FxSchematicPort *port = node->getInputPort(i);
		int j, linkCount = port->getLinkCount();
		for (j = 0; j < linkCount; j++) {
			SchematicLink *link = port->getLink(j);
			if (!link)
				continue;
			if (m_disconnectionLinks.isABridgeLink(link))
				continue;
			link->setHighlighted(value);
			link->update();
			m_highlightedLinks.push_back(link);
		}
	}

	FxSchematicPort *port = node->getOutputPort();
	if (port) {
		int linkCount = port->getLinkCount();
		for (i = 0; i < linkCount; i++) {
			SchematicLink *link = port->getLink(i);
			if (!link)
				continue;
			if (m_disconnectionLinks.isABridgeLink(link))
				continue;
			link->setHighlighted(value);
			link->update();
			m_highlightedLinks.push_back(link);
		}
	}
	port = node->getLinkPort();
	if (port) {
		SchematicLink *link = port->getLink(0);
		if (link && !m_disconnectionLinks.isABridgeLink(link)) {
			link->setHighlighted(value);
			link->update();
			m_highlightedLinks.push_back(link);
		}
	}
}

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

void FxSchematicScene::simulateDisconnectSelection(bool disconnect)
{
	if (m_selection->isEmpty())
		return;
	QList<TFxP> selectedFxs = m_selection->getFxs();
	if (selectedFxs.isEmpty())
		return;
	QMap<TFx *, bool> visitedFxs;
	int i;
	for (i = 0; i < selectedFxs.size(); i++)
		visitedFxs[selectedFxs[i].getPointer()] = false;

	TFx *inputFx = 0, *outputFx = 0;
	findBoundariesFxs(inputFx, outputFx, visitedFxs);
	FxSchematicNode *inputNode = m_table[inputFx];
	FxSchematicNode *outputNode = m_table[outputFx];
	assert(inputNode && outputNode);

	FxSchematicPort *inputPort = 0, *outputPort = 0;
	SchematicPort *otherInputPort = 0;
	QList<SchematicPort *> otherOutputPorts;
	if (inputNode->getInputPortCount() > 0) {
		inputPort = inputNode->getInputPort(0);
		if (inputPort) {
			SchematicLink *inputLink = inputPort->getLink(0);
			if (inputLink && !m_connectionLinks.isAnInputLink(inputLink)) {
				if (!m_disconnectionLinks.isAnInputLink(inputLink))
					m_disconnectionLinks.addInputLink(inputLink);
				otherInputPort = inputLink->getOtherPort(inputPort);
			}
		}
	}
	outputPort = outputNode->getOutputPort();
	if (outputPort) {
		for (i = 0; i < outputPort->getLinkCount(); i++) {
			SchematicLink *outputLink = outputPort->getLink(i);
			if (outputLink && !m_connectionLinks.isAnOutputLink(outputLink)) {
				if (!m_disconnectionLinks.isAnOutputLink(outputLink))
					m_disconnectionLinks.addOutputLink(outputLink);
				otherOutputPorts.push_back(outputLink->getOtherPort(outputPort));
			}
		}
	}
	if (disconnect) {
		m_disconnectionLinks.hideInputLinks();
		m_disconnectionLinks.hideOutputLinks();
	} else {
		m_disconnectionLinks.showInputLinks();
		m_disconnectionLinks.showOutputLinks();
		m_disconnectionLinks.removeInputLinks();
		m_disconnectionLinks.removeOutputLinks();
	}

	if (otherInputPort) {
		if (disconnect) {
			for (i = 0; i < otherOutputPorts.size(); i++)
				m_disconnectionLinks.addBridgeLink(otherOutputPorts[i]->makeLink(otherInputPort));
		} else
			m_disconnectionLinks.removeBridgeLinks(true);
	}
}

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

void FxSchematicScene::simulateInsertSelection(SchematicLink *link, bool connect)
{
	if (!link) {
		m_connectionLinks.showBridgeLinks();
		m_connectionLinks.hideInputLinks();
		m_connectionLinks.hideOutputLinks();
		m_connectionLinks.removeBridgeLinks();
		m_connectionLinks.removeInputLinks(true);
		m_connectionLinks.removeOutputLinks(true);
	} else {
		if (m_disconnectionLinks.isABridgeLink(link) || m_selection->isEmpty())
			return;

		if (connect) {
			m_connectionLinks.addBridgeLink(link);
			m_connectionLinks.hideBridgeLinks();
		} else {
			m_connectionLinks.showBridgeLinks();
			m_connectionLinks.removeBridgeLinks();
		}

		SchematicPort *inputPort = 0, *outputPort = 0;
		if (link) {
			if (link->getStartPort()->getType() == eFxInputPort) {
				inputPort = link->getStartPort();
				outputPort = link->getEndPort();
			} else {
				inputPort = link->getEndPort();
				outputPort = link->getStartPort();
			}
		}

		QMap<TFx *, bool> visitedFxs;
		QList<TFxP> selectedFxs = m_selection->getFxs();
		if (selectedFxs.isEmpty())
			return;
		int i;
		for (i = 0; i < selectedFxs.size(); i++)
			visitedFxs[selectedFxs[i].getPointer()] = false;

		TFx *inputFx = 0, *outputFx = 0;
		findBoundariesFxs(inputFx, outputFx, visitedFxs);
		FxSchematicNode *inputNode = m_table[inputFx];
		FxSchematicNode *outputNode = m_table[outputFx];
		assert(inputNode && outputNode);

		if (inputNode->getInputPortCount() > 0) {
			SchematicPort *inputNodePort = inputNode->getInputPort(0);
			if (inputNodePort && outputPort)
				m_connectionLinks.addInputLink(inputNodePort->makeLink(outputPort));
		}

		SchematicPort *outputNodePort = outputNode->getOutputPort();
		if (outputNodePort && inputPort)
			m_connectionLinks.addOutputLink(inputPort->makeLink(outputNodePort));

		if (connect) {
			m_connectionLinks.showInputLinks();
			m_connectionLinks.showOutputLinks();
		} else {
			m_connectionLinks.hideInputLinks();
			m_connectionLinks.hideOutputLinks();
			m_connectionLinks.removeInputLinks(true);
			m_connectionLinks.removeOutputLinks(true);
		}
	}
}
//------------------------------------------------------------
/*! in order to select nods after pasting the copied fx nodes from FxSelection
*/
void FxSchematicScene::selectNodes(QList<TFxP> &fxs)
{
	clearSelection();
	for (int i = 0; i < (int)fxs.size(); i++) {
		TFx *tempFx = fxs[i].getPointer();

		QMap<TFx *, FxSchematicNode *>::iterator it;
		it = m_table.find(tempFx);
		if (it == m_table.end())
			continue;

		it.value()->setSelected(true);
	}
	update();
}

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

void FxSchematicScene::updateNestedGroupEditors(FxSchematicNode *node, const QPointF &newPos)
{
	if (!node)
		return;
	QStack<int> groupIdStack = node->getFx()->getAttributes()->getGroupIdStack();
	int i;
	QRectF rect;
	for (i = 0; i < groupIdStack.size(); i++) {
		if (m_groupEditorTable.contains(groupIdStack[i])) {
			QRectF app = m_groupEditorTable[groupIdStack[i]]->sceneBoundingRect();
			if (rect.isEmpty())
				rect = app;
			else
#if QT_VERSION >= 0x050000
				rect = rect.united(app);
#else
				rect = rect.unite(app);
#endif
		}
	}
	QMap<TMacroFx *, FxSchematicMacroEditor *>::iterator it;
	for (it = m_macroEditorTable.begin(); it != m_macroEditorTable.end(); it++) {
		if (it.value()->contains(node)) {
			QRectF app = it.value()->sceneBoundingRect();
			if (rect.isEmpty())
				rect = app;
			else
#if QT_VERSION >= 0x050000
				rect = rect.united(app);
#else
				rect = rect.unite(app);
#endif
		}
	}
	node->setPos(newPos);
	for (i = 0; i < groupIdStack.size(); i++) {
		if (!m_groupEditorTable.contains(groupIdStack[i]))
			continue;
#if QT_VERSION >= 0x050000
		rect = rect.united(m_groupEditorTable[groupIdStack[i]]->sceneBoundingRect());
#else
		rect = rect.unite(m_groupEditorTable[groupIdStack[i]]->sceneBoundingRect());
#endif
		QRectF app = m_groupEditorTable[groupIdStack[i]]->boundingSceneRect();
		if (m_groupEditorTable[groupIdStack[i]]->scenePos() != app.topLeft())
			m_groupEditorTable[groupIdStack[i]]->setPos(app.topLeft());
	}
	for (it = m_macroEditorTable.begin(); it != m_macroEditorTable.end(); it++) {
		FxSchematicMacroEditor *editor = it.value();
		if (editor->contains(node)) {
			QRectF app = editor->sceneBoundingRect();
#if QT_VERSION >= 0x050000
			rect = rect.united(app);
#else
			rect = rect.unite(app);
#endif
			app = editor->boundingSceneRect();
			if (editor->scenePos() != app.topLeft())
				editor->setPos(app.topLeft());
		}
	}
	update(rect);
}

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

void FxSchematicScene::closeInnerMacroEditor(int groupId)
{
	assert(m_groupEditorTable.contains(groupId));
	QMap<TMacroFx *, FxSchematicMacroEditor *>::iterator it;
	for (it = m_macroEditorTable.begin(); it != m_macroEditorTable.end(); it++) {
		TMacroFx *macro = it.key();
		assert(macro);
		if (macro->getAttributes()->isContainedInGroup(groupId)) {
			macro->editMacro(false);
			macro->getAttributes()->closeEditingGroup(groupId);
		}
	}
}

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

void FxSchematicScene::resizeNodes(bool maximizedNode)
{
	//resize nodes
	m_gridDimension = maximizedNode ? eLarge : eSmall;
	m_xshHandle->getXsheet()->getFxDag()->setDagGridDimension(m_gridDimension);
	QMap<TFx *, FxSchematicNode *>::iterator it1;
	for (it1 = m_table.begin(); it1 != m_table.end(); it1++) {
		if (!it1.value())
			continue;
		it1.value()->resize(maximizedNode);
		TFx *fx = it1.value()->getFx();
		updatePositionOnResize(fx, maximizedNode);
	}
	QMap<int, FxGroupNode *>::iterator it2;
	for (it2 = m_groupedTable.begin(); it2 != m_groupedTable.end(); it2++) {
		if (!it2.value())
			continue;
		it2.value()->resize(maximizedNode);
		QList<TFxP> groupedFxs = it2.value()->getGroupedFxs();
		for (int i = 0; i < groupedFxs.size(); i++)
			updatePositionOnResize(groupedFxs[i].getPointer(), maximizedNode);
	}

	QMap<TMacroFx *, FxSchematicMacroEditor *>::iterator it3;
	for (it3 = m_macroEditorTable.begin(); it3 != m_macroEditorTable.end(); it3++) {
		if (!it3.value())
			continue;
		it3.value()->resizeNodes(maximizedNode);
	}
	updateScene();
}

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

void FxSchematicScene::updatePositionOnResize(TFx *fx, bool maximizedNode)
{
	TPointD oldPos = fx->getAttributes()->getDagNodePos();
	double oldPosY = oldPos.y - 25000;
	double newPosY = maximizedNode ? oldPosY * 2 : oldPosY * 0.5;
	fx->getAttributes()->setDagNodePos(TPointD(oldPos.x, newPosY + 25000));
}