Blob Blame Raw


#include "toonzqt/functionviewer.h"

// TnzQt includes
#include "toonzqt/functionselection.h"
#include "toonzqt/functionpanel.h"
#include "toonzqt/functiontreeviewer.h"
#include "toonzqt/functionsheet.h"
#include "toonzqt/functionsegmentviewer.h"
#include "toonzqt/functiontoolbar.h"
#include "toonzqt/swatchviewer.h"
#include "toonzqt/dvscrollwidget.h"

// TnzLib includes
#include "toonz/tstageobjecttree.h"
#include "toonz/txsheet.h"
#include "toonz/txsheethandle.h"
#include "toonz/tobjecthandle.h"
#include "toonz/tfxhandle.h"
#include "toonz/tcolumnhandle.h"
#include "toonz/fxdag.h"
#include "toonz/txshzeraryfxcolumn.h"
#include "toonz/tcolumnfx.h"
#include "toonz/toonzscene.h"
#include "toonz/tproject.h"
#include "toonz/tscenehandle.h"
#include "toonz/sceneproperties.h"

// TnzBase includes
#include "tparamcontainer.h"
#include "tunit.h"

// TnzCore includes
#include "tstopwatch.h"

// Qt includes
#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QBoxLayout>
#include <QPushButton>
#include <QStackedWidget>
#include <QLabel>
#include <QToolBar>
#include <QAction>

using namespace DVGui;

//=============================================================================
//
// FunctionPanel
//
//-----------------------------------------------------------------------------

#if QT_VERSION >= 0x050500
FunctionViewer::FunctionViewer(QWidget *parent, Qt::WindowFlags flags)
#else
FunctionViewer::FunctionViewer(QWidget *parent, Qt::WFlags flags)
#endif
	: QSplitter(parent), m_xshHandle(0), m_frameHandle(0), m_objectHandle(0), m_fxHandle(0), m_columnHandle(0), m_curve(0), m_selection(new FunctionSelection()), m_sceneHandle(0)
{
	setObjectName("FunctionEditor");

	// Prepare local timeline
	m_localFrame.setFrame(0);
	setFocusPolicy(Qt::NoFocus);

	m_treeView = new FunctionTreeView(this);
	m_functionGraph = new FunctionPanel(this);
	m_numericalColumns = new FunctionSheet();
	m_toolbar = new FunctionToolbar;
	m_segmentViewer = new FunctionSegmentViewer(this, m_numericalColumns, m_functionGraph);
	QWidget *leftPanel = new QWidget();
	QWidget *rightPanel = new QWidget();

	//----
	m_treeView->resize(150, m_treeView->size().height());
	m_treeView->setMinimumWidth(0);

	FunctionTreeModel *ftModel =
		dynamic_cast<FunctionTreeModel *>(m_treeView->model());
	m_functionGraph->setModel(ftModel);
	m_numericalColumns->setModel(ftModel);

	m_functionGraph->setSelection(getSelection());
	m_numericalColumns->setSelection(getSelection());

	m_numericalColumns->setViewer(this);

	m_toolbar->setSelection(m_selection);
	m_toolbar->setFocusPolicy(Qt::NoFocus);

	m_toolbar->setFrameHandle(&m_localFrame);		   // The function editor adopts an internal timeline
	m_functionGraph->setFrameHandle(&m_localFrame);	// synchronized among its various sub-widgets.
	m_numericalColumns->setFrameHandle(&m_localFrame); // In case an external m_frameHandle is specified,
													   // it synchronizes with that, too.

	//---- layout

	QVBoxLayout *leftLayout = new QVBoxLayout();
	leftLayout->setMargin(0);
	leftLayout->setSpacing(0);
	{
		leftLayout->addWidget(m_toolbar);
		leftLayout->addSpacing(36);
		leftLayout->addWidget(m_numericalColumns);
	}
	leftPanel->setLayout(leftLayout);

	addWidget(leftPanel);

	QVBoxLayout *rightLayout = new QVBoxLayout();
	rightLayout->setMargin(0);
	rightLayout->setSpacing(5);
	{
		rightLayout->addWidget(m_segmentViewer, 0);
		rightLayout->addWidget(m_treeView, 1);
	}
	rightPanel->setLayout(rightLayout);

	addWidget(rightPanel);

	//--- set the splitter's default size
	setSizes(QList<int>() << 500 << 200);
	setStretchFactor(0, 5);
	setStretchFactor(1, 2);

	//---- signal-slot connections
	bool ret = true;
	ret = ret && connect(m_toolbar, SIGNAL(numericalColumnToggled()), this, SLOT(toggleMode()));
	ret = ret && connect(ftModel, SIGNAL(activeChannelsChanged()), m_functionGraph, SLOT(update()));
	ret = ret && connect(ftModel, SIGNAL(activeChannelsChanged()), m_numericalColumns, SLOT(updateAll()));
	ret = ret && connect(ftModel, SIGNAL(curveChanged(bool)), m_treeView, SLOT(update()));
	ret = ret && connect(ftModel, SIGNAL(curveChanged(bool)), m_functionGraph, SLOT(update()));
	ret = ret && connect(ftModel, SIGNAL(curveChanged(bool)), m_numericalColumns, SLOT(updateAll()));
	ret = ret && connect(ftModel, SIGNAL(curveSelected(TDoubleParam *)), this, SLOT(onCurveSelected(TDoubleParam *)));
	ret = ret && connect(ftModel, SIGNAL(curveChanged(bool)), m_segmentViewer, SLOT(onCurveChanged()));
	ret = ret && connect(ftModel, SIGNAL(curveChanged(bool)), this, SLOT(onCurveChanged(bool)));
	ret = ret && connect(&m_localFrame, SIGNAL(frameSwitched()), this, SLOT(onFrameSwitched()));
	ret = ret && connect(getSelection(), SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()));
	ret = ret && connect(m_functionGraph, SIGNAL(keyframeSelected(double)), m_toolbar, SLOT(setFrame(double)));

	ret = ret && connect(m_treeView, SIGNAL(switchCurrentObject(TStageObject *)),
						 this, SLOT(doSwitchCurrentObject(TStageObject *)));
	ret = ret && connect(m_treeView, SIGNAL(switchCurrentFx(TFx *)),
						 this, SLOT(doSwitchCurrentFx(TFx *)));

	ret = ret && connect(ftModel, SIGNAL(currentChannelChanged(FunctionTreeModel::Channel *)), m_numericalColumns, SLOT(onCurrentChannelChanged(FunctionTreeModel::Channel *)));

	assert(ret);

	m_functionGraph->hide();
}

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

FunctionViewer::~FunctionViewer()
{
	delete m_selection;
	m_toolbar->setFrameHandle(0);
}

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

void FunctionViewer::showEvent(QShowEvent *)
{
	refreshModel();

	// Connect handles
	bool ret = true;

	if (m_xshHandle) {
		ret = connect(m_xshHandle, SIGNAL(xsheetChanged()), this, SLOT(refreshModel())) && ret;
		ret = connect(m_xshHandle, SIGNAL(xsheetSwitched()), this, SLOT(rebuildModel())) && ret;
	}

	if (m_frameHandle)
		ret = connect(m_frameHandle, SIGNAL(frameSwitched()), this, SLOT(propagateExternalSetFrame())) && ret;

	if (m_objectHandle) {
		ret = connect(m_objectHandle, SIGNAL(objectSwitched()), this, SLOT(onStageObjectSwitched())) && ret;
		ret = connect(m_objectHandle, SIGNAL(objectChanged(bool)), this, SLOT(onStageObjectChanged(bool))) && ret;
	}

	if (m_fxHandle)
		ret = connect(m_fxHandle, SIGNAL(fxSwitched()), this, SLOT(onFxSwitched())) && ret;

	//display animated channels when the scene is switched
	if (m_sceneHandle)
		ret = connect(m_sceneHandle, SIGNAL(sceneSwitched()), m_treeView, SLOT(displayAnimatedChannels())) && ret;

	assert(ret);

	m_treeView->setExpanded(m_treeView->model()->index(0, 0), true);
	m_treeView->setExpanded(m_treeView->model()->index(1, 0), true);

	FunctionTreeModel *ftm = static_cast<FunctionTreeModel *>(m_treeView->model());

	if (m_objectHandle) {
		assert(m_xshHandle);
		TXsheet *xsh = m_xshHandle->getXsheet();

		const TStageObjectId &objId = m_objectHandle->getObjectId();
		ftm->setCurrentStageObject((objId == TStageObjectId::NoneId) ? (TStageObject *)0 : xsh->getStageObject(objId));
	}

	if (m_fxHandle)
		ftm->setCurrentFx(m_fxHandle->getFx());
}

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

void FunctionViewer::hideEvent(QHideEvent *)
{
	if (m_xshHandle)
		m_xshHandle->disconnect(this);
	if (m_frameHandle)
		m_frameHandle->disconnect(this);
	if (m_objectHandle)
		m_objectHandle->disconnect(this);
	if (m_fxHandle)
		m_fxHandle->disconnect(this);
	if (m_sceneHandle)
		m_sceneHandle->disconnect(this);

	if (m_functionGraph->isVisible())
		m_functionGraph->hide();
}

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

void FunctionViewer::openContextMenu(TreeModel::Item *item, const QPoint &globalPos)
{
	m_treeView->openContextMenu(item, globalPos);
}

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

void FunctionViewer::rebuildModel()
{
	getSelection()->selectNone();
	onCurveSelected(0);
	m_functionGraph->getModel()->resetAll();
	refreshModel();
	m_treeView->displayAnimatedChannels();
}

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

void FunctionViewer::refreshModel()
{
	TXsheet *xsh = m_xshHandle ? m_xshHandle->getXsheet() : 0;

	m_functionGraph->getModel()->refreshData(xsh);

	if (xsh) {
		int rowCount = xsh->getFrameCount();
		m_numericalColumns->setRowCount(rowCount);
		m_numericalColumns->updateAll();

		ToonzScene *scene = xsh->getScene();
		if (!scene) // This seems wrong. It should rather be
			return; // asserted - though I'm not touching it now...

		TFilePath scenePath = scene->getScenePath().getParentDir();
		if (scene->isUntitled())
			scenePath = TProjectManager::instance()->getCurrentProject()->getScenesPath();

		m_treeView->setCurrentScenePath(scenePath);

		int distance, offset;
		scene->getProperties()->getMarkers(distance, offset);
		m_numericalColumns->setMarkRow(distance, offset);
	}

	m_treeView->updateAll();
	m_toolbar->setCurve(0);
}

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

void FunctionViewer::setXsheetHandle(TXsheetHandle *xshHandle)
{
	if (xshHandle == m_xshHandle)
		return;

	if (m_xshHandle)
		m_xshHandle->disconnect(this);

	m_xshHandle = xshHandle;
	m_segmentViewer->setXsheetHandle(xshHandle);

	if (m_xshHandle && isVisible()) {
		TXsheet *xsh = m_xshHandle->getXsheet();
		m_functionGraph->getModel()->refreshData(xsh);

		bool ret = connect(m_xshHandle, SIGNAL(xsheetChanged), this, SLOT(refreshModel()));
		assert(ret);
	}
}

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

void FunctionViewer::setFrameHandle(TFrameHandle *frameHandle)
{
	if (m_frameHandle == frameHandle)
		return;

	if (m_frameHandle)
		m_frameHandle->disconnect(this);

	m_frameHandle = frameHandle;

	if (m_frameHandle && isVisible()) {
		bool ret = connect(m_frameHandle, SIGNAL(frameSwitched()), this, SLOT(propagateExternalSetFrame()));
		assert(ret);
	}
}

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

void FunctionViewer::setObjectHandle(TObjectHandle *objectHandle)
{
	if (objectHandle == m_objectHandle)
		return;

	if (m_objectHandle)
		m_objectHandle->disconnect(this);

	m_objectHandle = objectHandle;

	if (m_objectHandle && isVisible()) {
		m_treeView->updateAll();

		bool ret = true;
		ret = connect(m_objectHandle, SIGNAL(objectSwitched()), this, SLOT(onStageObjectSwitched())) && ret;
		ret = connect(m_objectHandle, SIGNAL(objectChanged(bool)), this, SLOT(onStageObjectChanged(bool))) && ret;

		assert(ret);
	}

	FunctionTreeModel *ftModel = static_cast<FunctionTreeModel *>(m_treeView->model());
	if (ftModel)
		ftModel->setObjectHandle(objectHandle);
}
//-----------------------------------------------------------------------------

void FunctionViewer::setFxHandle(TFxHandle *fxHandle)
{
	if (fxHandle == m_fxHandle)
		return;

	if (m_fxHandle)
		m_fxHandle->disconnect(this);

	m_fxHandle = fxHandle;

	if (isVisible()) {
		m_treeView->updateAll();

		bool ret = connect(m_fxHandle, SIGNAL(fxSwitched()), this, SLOT(onFxSwitched()));
		assert(ret);
	}

	FunctionTreeModel *ftModel = static_cast<FunctionTreeModel *>(m_treeView->model());
	if (ftModel)
		ftModel->setFxHandle(fxHandle);
}

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

void FunctionViewer::setColumnHandle(TColumnHandle *columnHandle)
{
	if (columnHandle == m_columnHandle)
		return;

	m_columnHandle = columnHandle;

	if (isVisible())
		m_treeView->updateAll();
}

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

void FunctionViewer::onFrameSwitched()
{
	int frame = m_localFrame.getFrame();
	m_segmentViewer->setSegmentByFrame(m_curve, frame);

	if (m_frameHandle)
		m_frameHandle->setFrame(frame);
}

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

void FunctionViewer::toggleMode()
{
	if (isHidden())
		return;

	if (m_functionGraph->isVisible())
		m_functionGraph->hide();
	else
		m_functionGraph->show();
}

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

void FunctionViewer::onCurveChanged(bool isDragging)
{
	if (m_objectHandle)
		m_objectHandle->notifyObjectIdChanged(isDragging);

	//emit signal if the current channel belongs to Fx in order to update the preview fx
	if (m_fxHandle) {
		FunctionTreeModel *ftModel =
			dynamic_cast<FunctionTreeModel *>(m_treeView->model());
		if (ftModel) {
			FunctionTreeModel::Channel *currChan = ftModel->getCurrentChannel();
			if (currChan) {
				//カレントチャンネルがFxChannelGroupに含まれていたらEmit
				FxChannelGroup *fxChanGroup =
					dynamic_cast<FxChannelGroup *>(currChan->getChannelGroup());
				if (fxChanGroup)
					m_fxHandle->notifyFxChanged();
			}
		}
	}
}

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

void FunctionViewer::onCurveSelected(TDoubleParam *curve)
{
	m_curve = curve;
	m_toolbar->setCurve(curve);

	QPair<TDoubleParam *, int> selectedSegment = getSelection()->getSelectedSegment();
	if (selectedSegment.first)
		m_segmentViewer->setSegment(selectedSegment.first, selectedSegment.second);
	else
		m_segmentViewer->setSegment(m_curve, -1);
}

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

void FunctionViewer::onValueFieldChanged()
{
}

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

void FunctionViewer::onXsheetChanged()
{
	TXsheet *xsh = m_xshHandle->getXsheet();
	int rowCount = xsh->getFrameCount();
	m_numericalColumns->setRowCount(rowCount);
}

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

void FunctionViewer::onStageObjectSwitched()
{
	TXsheet *xsh = m_xshHandle->getXsheet();

	const TStageObjectId &objId = m_objectHandle->getObjectId();
	TStageObject *obj = (objId == TStageObjectId::NoneId) ? (TStageObject *)0 : xsh->getStageObject(objId);

	static_cast<FunctionTreeModel *>(m_treeView->model())->setCurrentStageObject(obj);

	m_treeView->updateAll();
	m_functionGraph->update();
}

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

void FunctionViewer::onStageObjectChanged(bool isDragging)
{
	TXsheet *xsh = m_xshHandle->getXsheet();

	const TStageObjectId &objId = m_objectHandle->getObjectId();
	TStageObject *obj = (objId == TStageObjectId::NoneId) ? (TStageObject *)0 : xsh->getStageObject(objId);

	static_cast<FunctionTreeModel *>(m_treeView->model())->setCurrentStageObject(obj);

	if (!isDragging)
		m_treeView->updateAll();

	m_functionGraph->update();
}

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

void FunctionViewer::onFxSwitched()
{
	TFx *fx = m_fxHandle->getFx();
	TZeraryColumnFx *zfx = dynamic_cast<TZeraryColumnFx *>(fx);
	if (zfx)
		fx = zfx->getZeraryFx();
	static_cast<FunctionTreeModel *>(m_treeView->model())->setCurrentFx(fx);
	m_treeView->updateAll();
	m_functionGraph->update();
}

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

void FunctionViewer::onSelectionChanged()
{
	QPair<TDoubleParam *, int> selectedSegment = getSelection()->getSelectedSegment();
	if (selectedSegment.first) {
		if (selectedSegment.first != m_curve) {
			m_curve = selectedSegment.first;
			m_toolbar->setCurve(selectedSegment.first);
		}
		m_segmentViewer->setSegment(selectedSegment.first, selectedSegment.second);
	} else {
		m_segmentViewer->setSegment(m_curve, -1);
	}
	// update curves
	if (m_functionGraph->isVisible() && !m_functionGraph->hasFocus())
		m_functionGraph->update();
}

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

void FunctionViewer::doSwitchCurrentObject(TStageObject *obj)
{
	TStageObjectId id = obj->getId();
	if (id.isColumn())
		m_columnHandle->setColumnIndex(id.getIndex());
	else
		m_objectHandle->setObjectId(id);
}

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

void FunctionViewer::doSwitchCurrentFx(TFx *fx)
{
	if (!fx) {
		m_fxHandle->setFx(0);
		return;
	}
	if (fx->isZerary()) {
		TXsheet *xsh = m_xshHandle->getXsheet();
		int i, columnCount = xsh->getColumnCount();
		for (i = 0; i < columnCount; i++) {
			TXshColumn *column = xsh->getColumn(i);
			TXshZeraryFxColumn *zfx = dynamic_cast<TXshZeraryFxColumn *>(xsh->getColumn(i));
			if (!zfx)
				continue;
			if (zfx->getZeraryColumnFx()->getZeraryFx() == fx) {
				fx = zfx->getZeraryColumnFx();
				m_columnHandle->setColumnIndex(i);
				break;
			}
		}
	}
	// Forbid update of the swatch upon column switch. This would generate
	// a useless render...
	SwatchViewer::suspendRendering(true, false);

	int columnIndex = fx->getReferenceColumnIndex();
	if (columnIndex >= 0)
		m_columnHandle->setColumnIndex(columnIndex);

	SwatchViewer::suspendRendering(false);
	m_fxHandle->setFx(fx);
	emit editObject();
}

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

void FunctionViewer::propagateExternalSetFrame()
{
	assert(m_frameHandle);
	m_localFrame.setFrame(m_frameHandle->getFrame());
}

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

void FunctionViewer::addParameter(
	TParam *parameter, const TFilePath &folder)
{
	static_cast<FunctionTreeModel *>(m_treeView->model())->addParameter(parameter, folder);
}

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

void FunctionViewer::setFocusColumnsOrGraph()
{
	m_numericalColumns->setFocus();
}

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

void FunctionViewer::clearFocusColumnsAndGraph()
{
	m_functionGraph->clearFocus();
	m_numericalColumns->clearFocus();
}

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

bool FunctionViewer::columnsOrGraphHasFocus()
{
	return m_functionGraph->hasFocus() || m_numericalColumns->anyWidgetHasFocus() || m_toolbar->anyWidgetHasFocus() || m_segmentViewer->anyWidgetHasFocus();
}

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

void FunctionViewer::setSceneHandle(TSceneHandle *sceneHandle)
{
	m_sceneHandle = sceneHandle;
}

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

bool FunctionViewer::isExpressionPageActive()
{
	return m_segmentViewer->isExpressionPageActive();
}