Blob Blame Raw

//ToonzCore includes
#include "tmsgcore.h"

//ToonzLib includes
#include "toonz/txshlevelhandle.h"
#include "toonz/tscenehandle.h"
#include "toonz/toonzscene.h"
#include "toonz/toonzfolders.h"

//ToonzQt includes
#include "toonzqt/gutil.h"
#include "toonzqt/doublefield.h"

//Toonz includes
#include "tapp.h"
#include "cleanupsettingsmodel.h"
#include "cleanuppaletteviewer.h"
#include "pane.h"
#include "menubarcommandids.h"
#include "floatingpanelcommand.h"

//Qt includes
#include <QComboBox>
#include <QCheckBox>
#include <QLabel>
#include <QPushButton>
#include <QHBoxLayout>

#include "cleanupsettingspane.h"

using namespace DVGui;

/*
"Save In" フィールドのためのFileField。browseDirectoryを再実装して、フィールドが空欄のときは、
カレントレベル(Scan画像。TIF等)の入っているフォルダの1つ上をデフォルトフォルダにして開くようにしたい。
*/
void CleanupSaveInField::browseDirectory()
{
	if (!m_fileBrowseButton->hasFocus())
		return;
	QString directory = QString();

	if (!m_browserPopupController)
		return;

	/*
	ここで、m_lastSelectedPathが空のとき、カレントレベルがScan画像の場合、
	そのファイルの入っているフォルダの1つ上のフォルダを初期フォルダにする
	*/
	QString initialFolder = m_lastSelectedPath;
	if (initialFolder.isEmpty()) {
		/*--- 親Widgetを取得する ---*/
		CleanupSettingsPane *parentCSP = dynamic_cast<CleanupSettingsPane *>(parentWidget());
		if (parentCSP) {
			TFilePath lastSelectedPath = parentCSP->getLastSelectedPath();
			if (!lastSelectedPath.isEmpty()) {
				/*---- 親Widgetのm_lastSelectedPathが、CLNファイルの見込み所在地なので、その1つ上のフォルダを初期フォルダにする。---*/
				initialFolder = QString::fromStdWString(lastSelectedPath.getParentDir().getParentDir().getWideString());
			}
		}
	}

	m_browserPopupController->openPopup(QStringList(), true, initialFolder);
	if (m_browserPopupController->isExecute())
		directory = m_browserPopupController->getPath();

	if (!directory.isEmpty()) {
		setPath(directory);
		m_lastSelectedPath = directory;
		emit pathChanged();
		return;
	}
}

//=============================================================================
// CleanupSettingsPane
//-----------------------------------------------------------------------------

CleanupSettingsPane::CleanupSettingsPane(QWidget *parent)
	: QFrame(parent), m_attached(false)
{
	//Rotate&Flip
	QFrame *rotFlipFrame = new QFrame(this);
	m_rotateOm = new QComboBox(this);
	m_flipX = new QCheckBox(tr("Horizontal"), this);
	m_flipY = new QCheckBox(tr("Vertical"), this);
	//Camera
	QFrame *cameraFrame = new QFrame(this);
	m_cameraWidget = new CleanupCameraSettingsWidget();
	//LineProcessing
	QFrame *lineProcFrame = new QFrame(this);
	m_antialias = new QComboBox(this);
	m_sharpness = new DoubleField(this);
	m_despeckling = new IntField(this);
	m_aaValueLabel = new QLabel(tr("MLAA Intensity:"));
	m_aaValue = new IntField(this);
	m_lineProcessing = new QComboBox(this);
	m_paletteViewer = new CleanupPaletteViewer(this);
	m_pathField = new CleanupSaveInField(this, QString(""));

	QPushButton *saveBtn = new QPushButton(tr("Save"));
	QPushButton *loadBtn = new QPushButton(tr("Load"));
	QPushButton *resetBtn = new QPushButton(tr("Reset"));

	//Rotate&Flip
	rotFlipFrame->setObjectName("CleanupSettingsFrame");
	QStringList rotate;
	rotate << "0"
		   << "90"
		   << "180"
		   << "270";
	m_rotateOm->addItems(rotate);
	//Camera
	cameraFrame->setObjectName("CleanupSettingsFrame");
	m_cameraWidget->setCameraPresetListFile(ToonzFolder::getReslistPath(true));
	//LineProcessing
	lineProcFrame->setObjectName("CleanupSettingsFrame");
	QStringList items;
	items << tr("Standard") << tr("None") << tr("Morphological");
	m_antialias->addItems(items);

	items.clear();
	items << tr("Greyscale") << tr("Color");
	m_lineProcessing->addItems(items);

	m_sharpness->setValues(90, 0, 100);
	m_despeckling->setValues(2, 0, 20);
	m_aaValue->setValues(70, 0, 100);

	//  Model-related stuff
	CleanupSettingsModel *model = CleanupSettingsModel::instance();
	m_backupParams.assign(model->getCurrentParameters(), false);

	//----layout
	QVBoxLayout *mainLay = new QVBoxLayout();
	mainLay->setSpacing(2);
	mainLay->setMargin(5);
	{
		//Rotate&Flip
		QGridLayout *rotFlipLay = new QGridLayout();
		rotFlipLay->setMargin(5);
		rotFlipLay->setSpacing(3);
		{
			rotFlipLay->addWidget(new QLabel(tr("Rotate")), 0, 0, Qt::AlignRight | Qt::AlignVCenter);
			rotFlipLay->addWidget(m_rotateOm, 0, 1, 1, 2);
			rotFlipLay->addWidget(new QLabel(tr("Flip")), 1, 0, Qt::AlignRight | Qt::AlignVCenter);
			rotFlipLay->addWidget(m_flipX, 1, 1);
			rotFlipLay->addWidget(m_flipY, 1, 2);
		}
		rotFlipLay->setColumnStretch(0, 0);
		rotFlipLay->setColumnStretch(1, 0);
		rotFlipLay->setColumnStretch(2, 1);
		rotFlipFrame->setLayout(rotFlipLay);
		mainLay->addWidget(rotFlipFrame, 0);

		//Camera
		QVBoxLayout *cleanupCameraFrameLay = new QVBoxLayout();
		cleanupCameraFrameLay->setMargin(0);
		cleanupCameraFrameLay->setSpacing(0);
		{
			cleanupCameraFrameLay->addWidget(m_cameraWidget);
		}
		cameraFrame->setLayout(cleanupCameraFrameLay);
		mainLay->addWidget(cameraFrame, 0);

		//Cleanup Palette
		QGridLayout *lineProcLay = new QGridLayout();
		lineProcLay->setMargin(5);
		lineProcLay->setSpacing(3);
		{
			lineProcLay->addWidget(new QLabel(tr("Line Processing:")), 0, 0, Qt::AlignRight | Qt::AlignVCenter);
			lineProcLay->addWidget(m_lineProcessing, 0, 1);
			lineProcLay->addWidget(new QLabel(tr("Antialias:")), 1, 0, Qt::AlignRight | Qt::AlignVCenter);
			lineProcLay->addWidget(m_antialias, 1, 1);
			lineProcLay->addWidget(new QLabel(tr("Sharpness:")), 2, 0, Qt::AlignRight | Qt::AlignVCenter);
			lineProcLay->addWidget(m_sharpness, 2, 1);
			lineProcLay->addWidget(new QLabel(tr("Despeckling:")), 3, 0, Qt::AlignRight | Qt::AlignVCenter);
			lineProcLay->addWidget(m_despeckling, 3, 1);
			lineProcLay->addWidget(m_aaValueLabel, 4, 0, Qt::AlignRight | Qt::AlignVCenter);
			lineProcLay->addWidget(m_aaValue, 4, 1);

			lineProcLay->addWidget(m_paletteViewer, 5, 0, 1, 2);
		}
		lineProcLay->setRowStretch(0, 0);
		lineProcLay->setRowStretch(1, 0);
		lineProcLay->setRowStretch(2, 0);
		lineProcLay->setRowStretch(3, 0);
		lineProcLay->setRowStretch(4, 0);
		lineProcLay->setRowStretch(5, 1);
		lineProcLay->setColumnStretch(0, 0);
		lineProcLay->setColumnStretch(1, 1);

		lineProcFrame->setLayout(lineProcLay);

		mainLay->addWidget(lineProcFrame, 100);

		//Bottom Parts
		QHBoxLayout *pathLay = new QHBoxLayout();
		pathLay->setMargin(0);
		pathLay->setSpacing(3);
		{
			pathLay->addWidget(new QLabel("Save In"), 0);
			pathLay->addWidget(m_pathField);
		}
		mainLay->addLayout(pathLay, 0);

		mainLay->addSpacing(5);

		QHBoxLayout *saveLoadLay = new QHBoxLayout();
		saveLoadLay->setMargin(0);
		saveLoadLay->setSpacing(1);
		{
			saveLoadLay->addWidget(saveBtn);
			saveLoadLay->addWidget(loadBtn);
			saveLoadLay->addWidget(resetBtn);
		}
		mainLay->addLayout(saveLoadLay, 0);
	}
	setLayout(mainLay);

	//-----signal-slot connections
	bool ret = true;
	ret = ret && connect(m_rotateOm, SIGNAL(activated(int)), SLOT(onGenericSettingsChange()));
	ret = ret && connect(m_flipX, SIGNAL(stateChanged(int)), SLOT(onGenericSettingsChange()));
	ret = ret && connect(m_flipY, SIGNAL(stateChanged(int)), SLOT(onGenericSettingsChange()));
	ret = ret && connect(m_pathField, SIGNAL(pathChanged()), SLOT(onPathChange()));
	ret = ret && connect(m_sharpness, SIGNAL(valueChanged(bool)), SLOT(onSharpnessChange(bool)));
	ret = ret && connect(m_antialias, SIGNAL(activated(int)), SLOT(onGenericSettingsChange()));
	ret = ret && connect(m_lineProcessing, SIGNAL(activated(int)), SLOT(onGenericSettingsChange()));
	ret = ret && connect(m_despeckling, SIGNAL(valueChanged(bool)), SLOT(onGenericSettingsChange()));
	ret = ret && connect(m_aaValue, SIGNAL(valueChanged(bool)), SLOT(onGenericSettingsChange()));
	ret = ret && connect(TApp::instance()->getCurrentLevel(),
						 SIGNAL(xshLevelSwitched(TXshLevel *)), SLOT(onLevelSwitched()));
	ret = ret && connect(m_cameraWidget, SIGNAL(cleanupSettingsChanged()), SLOT(onGenericSettingsChange()));

	ret = ret && connect(saveBtn, SIGNAL(pressed()), this, SLOT(onSaveSettings()));

	ret = ret && connect(loadBtn, SIGNAL(pressed()), model, SLOT(promptLoad()));
	ret = ret && connect(resetBtn, SIGNAL(pressed()), this, SLOT(onRestoreSceneSettings()));

	assert(ret);
}

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

void CleanupSettingsPane::showEvent(QShowEvent *se)
{
	QFrame::showEvent(se);

	if (!m_attached) {

		m_attached = true;
		//Should ensure that swatch is off...
		CleanupSettingsModel *model = CleanupSettingsModel::instance();

		model->attach(CleanupSettingsModel::LISTENER);

		bool ret = true;
		ret = ret && connect(model, SIGNAL(imageSwitched()), this, SLOT(onImageSwitched()));
		ret = ret && connect(model, SIGNAL(modelChanged(bool)), this, SLOT(updateGui(bool)));
		ret = ret && connect(model, SIGNAL(clnLoaded()), this, SLOT(onClnLoaded()));
		assert(ret);

		m_cameraWidget->setCurrentLevel(TApp::instance()->getCurrentLevel()->getLevel());

		updateGui(false);
		onImageSwitched();
		onClnLoaded();
	}
}

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

void CleanupSettingsPane::hideEvent(QHideEvent *he)
{
	// Surprisingly enough, it seems that Qt may trigger hideEvents for widgets that
	// have never been shown - this is why we need to check a user-made bool to know whether
	// model attachment has happened... and no, detaching without attaching is *BAD*

	if (m_attached) {
		m_attached = false;
		//Should put swatch to off...
		CleanupSettingsModel *model = CleanupSettingsModel::instance();

		model->detach(CleanupSettingsModel::LISTENER);

		bool ret = true;
		ret = ret && disconnect(model, SIGNAL(imageSwitched()), this, SLOT(onImageSwitched()));
		ret = ret && disconnect(model, SIGNAL(modelChanged(bool)), this, SLOT(updateGui(bool)));
		ret = ret && disconnect(model, SIGNAL(clnLoaded()), this, SLOT(onClnLoaded()));
		assert(ret);
	}

	QFrame::hideEvent(he);
}

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

void CleanupSettingsPane::updateGui(bool needsPostProcess)
{
	CleanupParameters *params = CleanupSettingsModel::instance()->getCurrentParameters();

	updateGui(params, &m_backupParams);

	m_backupParams.assign(params, false);

	if (needsPostProcess)
		postProcess();
}

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

void CleanupSettingsPane::updateGui(CleanupParameters *params, CleanupParameters *oldParams)
{
	m_rotateOm->setCurrentIndex(params->m_rotate / 90);
	m_flipX->setChecked(params->m_flipx);
	m_flipY->setChecked(params->m_flipy);

	m_path = params->m_path;
	m_pathField->setPath(toQString(m_path));

	m_lineProcessing->setCurrentIndex((params->m_lineProcessingMode == lpGrey) ? 0 : 1);
	m_antialias->setCurrentIndex(params->m_postAntialias ? 2 : params->m_noAntialias ? 1 : 0);
	m_sharpness->setValue(params->m_sharpness);
	m_despeckling->setValue(params->m_despeckling);
	m_aaValue->setValue(params->m_aaValue);

	updateVisibility();

	blockSignals(true);
	m_cameraWidget->setFields(params);
	blockSignals(false);

	if (params->m_lineProcessingMode != oldParams->m_lineProcessingMode ||
		params->m_camera.getRes() != oldParams->m_camera.getRes() ||
		params->m_camera.getSize() != oldParams->m_camera.getSize() ||
		params->m_closestField != oldParams->m_closestField) {
		updateImageInfo();
	}
}

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

void CleanupSettingsPane::updateImageInfo()
{
	CleanupSettingsModel *model = CleanupSettingsModel::instance();
	CleanupParameters *params = model->getCurrentParameters();

	TDimension outRes(0, 0);
	TPointD outDpi;

	params->getOutputImageInfo(outRes, outDpi.x, outDpi.y);
	m_cameraWidget->setImageInfo(outRes.lx, outRes.ly, outDpi.x, outDpi.y);

	TXshSimpleLevel *sl;
	TFrameId fid;
	model->getCleanupFrame(sl, fid);

	ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
	TFilePath outputPath(sl ? scene->decodeFilePath(model->getOutputPath(sl, params)) : TFilePath());

	m_cameraWidget->setImageInfo(outputPath);

	if (!parentWidget())
		return;

	if (model->clnPath().isEmpty())
		parentWidget()->setWindowTitle("Cleanup Settings (Global)");
	else
		parentWidget()->setWindowTitle("Cleanup Settings: " + toQString(model->clnPath().withoutParentDir()));
}

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

void CleanupSettingsPane::updateVisibility()
{
	bool lpGrey = (m_lineProcessing->currentIndex() == 0);
	bool MLAA = (m_antialias->currentIndex() == 2);

	m_antialias->setVisible(true);
	m_sharpness->setVisible(true);
	m_despeckling->setVisible(true);
	m_aaValueLabel->setVisible(MLAA);
	m_aaValue->setVisible(MLAA);

	m_paletteViewer->setMode(lpGrey);
	m_paletteViewer->setContrastEnabled(m_antialias->currentIndex() == 0);

	m_paletteViewer->setVisible(true);
}

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

void CleanupSettingsPane::onImageSwitched()
{
	updateImageInfo();
}

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

void CleanupSettingsPane::onPreviewDataChanged()
{
	TRasterImageP original, transformed;
	TAffine transform;

	CleanupSettingsModel::instance()->getPreviewData(original, transformed, transform);
}

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

void CleanupSettingsPane::postProcess()
{
	//m_swatch->updateCleanupped();
}

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

void CleanupSettingsPane::onClnLoaded()
{
	const TFilePath &fp = CleanupSettingsModel::instance()->clnPath();
	if (fp.isEmpty())
		setWindowTitle(tr("Cleanup Settings"));
	else
		setWindowTitle(tr("Cleanup Settings: %1").arg(toQString(fp.withoutParentDir())));
}

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

void CleanupSettingsPane::onRestoreSceneSettings()
{
	CleanupSettingsModel *model = CleanupSettingsModel::instance();

	model->saveSettingsIfNeeded();
	model->restoreGlobalSettings();
}

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

void CleanupSettingsPane::onGenericSettingsChange()
{
	CleanupSettingsModel *model = CleanupSettingsModel::instance();
	CleanupParameters *params = model->getCurrentParameters();

	params->m_rotate = m_rotateOm->currentIndex() * 90;
	params->m_flipx = m_flipX->isChecked();
	params->m_flipy = m_flipY->isChecked();

	//------

	params->m_lineProcessingMode = (int)((m_lineProcessing->currentIndex() == 0) ? lpGrey : lpColor);
	params->m_noAntialias = (m_antialias->currentIndex() > 0);
	params->m_postAntialias = (m_antialias->currentIndex() == 2);
	params->m_despeckling = m_despeckling->getValue();
	params->m_aaValue = m_aaValue->getValue();

	//------

	m_cameraWidget->getFields(model->getCurrentParameters());

	model->commitChanges();
}

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

void CleanupSettingsPane::onPathChange()
{
	CleanupSettingsModel *model = CleanupSettingsModel::instance();
	CleanupParameters *params = model->getCurrentParameters();

	m_path = params->m_path = TFilePath(m_pathField->getPath().toStdWString());

	model->commitChanges();
}

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

void CleanupSettingsPane::onSharpnessChange(bool dragging = false)
{
	if (dragging)
		return;

	CleanupSettingsModel *model = CleanupSettingsModel::instance();

	model->getCurrentParameters()->m_sharpness = m_sharpness->getValue();
	model->commitChanges();
}

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

void CleanupSettingsPane::onLevelSwitched()
{
	m_cameraWidget->setCurrentLevel(TApp::instance()->getCurrentLevel()->getLevel());
}

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

void CleanupSettingsPane::onSaveSettings()
{
	/*--- Clueaup保存先を指定していないとエラーを返す ---*/
	if (m_pathField->getPath().isEmpty()) {
		DVGui::warning("Please fill the Save In field.");
		return;
	}
	CleanupSettingsModel::instance()->promptSave();
}