Blob Blame History Raw


#include "svnupdatedialog.h"

// Tnz6 includes
#include "tapp.h"
#include "versioncontrolwidget.h"

// TnzQt includes
#include "toonzqt/gutil.h"

// TnzLib includes
#include "toonz/txshsimplelevel.h"
#include "toonz/toonzscene.h"

// Qt includes
#include <QWidget>
#include <QPushButton>
#include <QScrollBar>
#include <QBoxLayout>
#include <QLabel>
#include <QMovie>
#include <QTextEdit>
#include <QCheckBox>
#include <QRegExp>
#include <QDir>
#include <QMainWindow>

//=============================================================================
// SVNUpdateDialog
//-----------------------------------------------------------------------------

SVNUpdateDialog::SVNUpdateDialog(QWidget *parent, const QString &workingDir,
								 const QStringList &files, int sceneIconsCount,
								 bool isFolderOnly,
								 bool updateToRevision,
								 bool nonRecursive)
	: Dialog(TApp::instance()->getMainWindow(), true, false), m_updateSceneContentsCheckBox(0), m_workingDir(workingDir), m_files(files), m_updateToRevision(updateToRevision), m_nonRecursive(nonRecursive), m_sceneIconsCount(sceneIconsCount), m_someSceneIsMissing(false)
{
	setModal(false);
	setMinimumSize(300, 180);
	setAttribute(Qt::WA_DeleteOnClose, true);
	setWindowTitle(tr("Version Control: Update"));

	QWidget *container = new QWidget;

	QVBoxLayout *mainLayout = new QVBoxLayout;
	mainLayout->setAlignment(Qt::AlignHCenter);
	mainLayout->setMargin(0);

	QHBoxLayout *hLayout = new QHBoxLayout;

	m_waitingLabel = new QLabel;
	QMovie *waitingMove = new QMovie(":Resources/waiting.gif");
	waitingMove->setParent(this);

	m_waitingLabel->setMovie(waitingMove);
	waitingMove->setCacheMode(QMovie::CacheAll);
	waitingMove->start();

	m_textLabel = new QLabel(tr("Getting repository status..."));

	hLayout->addStretch();
	hLayout->addWidget(m_waitingLabel);
	hLayout->addWidget(m_textLabel);
	hLayout->addStretch();

	mainLayout->addLayout(hLayout);

	m_output = new QTextEdit;
	m_output->setTextInteractionFlags(Qt::NoTextInteraction);
	m_output->setReadOnly(true);
	m_output->hide();

	mainLayout->addWidget(m_output);

	m_conflictWidget = new ConflictWidget;
	m_conflictWidget->hide();

	mainLayout->addWidget(m_conflictWidget);

	m_dateChooserWidget = new DateChooserWidget;
	m_dateChooserWidget->hide();

	mainLayout->addWidget(m_dateChooserWidget);

	if (!isFolderOnly) {
		QHBoxLayout *checkBoxLayout = new QHBoxLayout;
		checkBoxLayout->setMargin(0);
		m_updateSceneContentsCheckBox = new QCheckBox(this);
		m_updateSceneContentsCheckBox->setChecked(false);
		m_updateSceneContentsCheckBox->setText(tr("Get Scene Contents"));
		m_updateSceneContentsCheckBox->hide();
		connect(m_updateSceneContentsCheckBox, SIGNAL(toggled(bool)), this, SLOT(onUpdateSceneContentsToggled(bool)));

		checkBoxLayout->addStretch();
		checkBoxLayout->addWidget(m_updateSceneContentsCheckBox);
		checkBoxLayout->addStretch();

		mainLayout->addSpacing(10);
		mainLayout->addLayout(checkBoxLayout);
	}

	container->setLayout(mainLayout);

	beginHLayout();
	addWidget(container, false);
	endHLayout();

	m_updateButton = new QPushButton(tr("Update"));
	m_updateButton->hide();
	if (m_updateToRevision)
		connect(m_updateButton, SIGNAL(clicked()), this, SLOT(onUpdateToRevisionButtonClicked()));
	else
		connect(m_updateButton, SIGNAL(clicked()), this, SLOT(onUpdateButtonClicked()));

	m_closeButton = new QPushButton(tr("Close"));
	m_closeButton->setEnabled(false);
	connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close()));

	m_cancelButton = new QPushButton(tr("Cancel"));
	connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(reject()));

	addButtonBarWidget(m_updateButton, m_closeButton, m_cancelButton);

	// 0. Connect for svn errors (that may occurs everythings)
	connect(&m_thread, SIGNAL(error(const QString &)), this, SLOT(onError(const QString &)));

	// 1. Getting status
	connect(&m_thread, SIGNAL(statusRetrieved(const QString &)), this, SLOT(onStatusRetrieved(const QString &)));
	m_thread.getSVNStatus(m_workingDir, m_files, true, false, true);
}

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

void SVNUpdateDialog::onStatusRetrieved(const QString &xmlResponse)
{
	m_waitingLabel->hide();

	SVNStatusReader sr(xmlResponse);
	m_status = sr.getStatus();

	checkFiles();

	// Check if a scene files is found
	int fileSize = m_filesToUpdate.size();
	for (int i = 0; i < fileSize; i++) {
		if (m_filesToUpdate.at(i).endsWith(".tnz")) {
			if (fileSize == 1)
				m_textLabel->setText(tr("%1 items to update.").arg(m_filesToUpdate.size() + m_sceneResources.size()));
			else
				m_textLabel->setText(tr("%1 items to update.").arg(m_filesToUpdate.size() + m_sceneResources.size() - m_sceneIconsCount));
			if (m_updateSceneContentsCheckBox && !m_someSceneIsMissing)
				m_updateSceneContentsCheckBox->show();
			m_updateButton->show();
			m_closeButton->hide();
			return;
		}
	}

	if (m_updateToRevision) {
		if (m_filesWithConflict.count() > 0) {
			m_textLabel->setText(tr("Some items are currently modified in your working copy.\nPlease commit or revert changes first."));
			m_textLabel->show();
			switchToCloseButton();
			return;
		}

		m_textLabel->setText(tr("Update to:"));
		m_textLabel->show();
		m_dateChooserWidget->show();
		m_updateButton->show();
		m_closeButton->hide();

		adjustSize();
	} else {
		// Resolve first files with conflict
		if (m_filesWithConflict.count() > 0) {
			m_textLabel->setText(tr("Some conflict found. Select.."));
			m_textLabel->show();
			m_updateButton->setEnabled(false);
			m_updateButton->show();
			m_closeButton->hide();

			QStringList fileswithConflict;
			int fileswithConflictCount = m_filesWithConflict.size();
			for (int i = 0; i < fileswithConflictCount; i++)
				fileswithConflict.append(m_filesWithConflict.at(i));
			m_conflictWidget->setFiles(fileswithConflict);
			m_conflictWidget->show();
			adjustSize();
			connect(m_conflictWidget, SIGNAL(allConflictSetted()), this, SLOT(onConflictSetted()));
		} else
			updateFiles();
	}
}

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

void SVNUpdateDialog::checkFiles()
{
	int statusCount = m_status.size();
	for (int i = 0; i < statusCount; i++) {
		SVNStatus s = m_status.at(i);
		if (s.m_path == "." || s.m_path == "..")
			continue;
		if ((m_updateToRevision && s.m_item == "modified") ||
			(s.m_item == "modified" && s.m_repoStatus == "modified"))
			m_filesWithConflict.prepend(s.m_path);
		else if (s.m_item == "none" || s.m_item == "missing" ||
				 s.m_repoStatus == "modified" || s.m_repoStatus == "modified") {
			if (s.m_path.endsWith(".tnz") && (s.m_item == "missing" || s.m_item == "none" && s.m_repoStatus == "added")) {
				TFilePath scenePath = TFilePath(m_workingDir.toStdWString()) + s.m_path.toStdWString();
				TFilePath iconPath = ToonzScene::getIconPath(scenePath);
				QDir dir(m_workingDir);
#ifdef MACOSX
				m_filesToUpdate.append(dir.relativeFilePath(toQString(iconPath)));
#else
				m_filesToUpdate.append(dir.relativeFilePath(toQString(iconPath)).replace("/", "\\"));
#endif
				m_sceneIconsCount++;
				m_someSceneIsMissing = true;
			}
			if (m_files.count() == 1 || m_files.contains(s.m_path))
				m_filesToUpdate.prepend(s.m_path);
		}
	}
}

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

void SVNUpdateDialog::updateFiles()
{
	if (m_filesToUpdate.count() == 0) {
		QString msg = QString(tr("No items to update."));
		m_textLabel->setText(msg);
		m_textLabel->show();
		switchToCloseButton();
		return;
	}

	if (m_updateSceneContentsCheckBox)
		m_updateSceneContentsCheckBox->hide();
	m_updateButton->setEnabled(false);
	m_waitingLabel->hide();
	m_textLabel->hide();
	m_output->show();

	QStringList args;
	args << "update";

	int filesCount = m_filesToUpdate.count();
	for (int i = 0; i < filesCount; i++) {
		QString file = m_filesToUpdate.at(i);
		if (!file.isEmpty())
			args << file;
	}

	int resourceCount = m_sceneResources.size();
	for (int i = 0; i < resourceCount; i++)
		args << m_sceneResources.at(i);

	if (m_nonRecursive)
		args << "--non-recursive";

	m_thread.disconnect(SIGNAL(done(const QString &)));
	connect(&m_thread, SIGNAL(outputRetrieved(const QString &)), SLOT(addOutputText(const QString &)));
	connect(&m_thread, SIGNAL(done(const QString &)), SLOT(onUpdateDone(const QString &)));
	m_thread.executeCommand(m_workingDir, "svn", args, false);
}

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

void SVNUpdateDialog::switchToCloseButton()
{
	if (m_updateSceneContentsCheckBox)
		m_updateSceneContentsCheckBox->hide();
	m_cancelButton->hide();
	m_updateButton->hide();
	m_closeButton->show();
	m_closeButton->setEnabled(true);
}

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

void SVNUpdateDialog::addOutputText(const QString &text)
{
	QRegExp regExp("Updated to revision (\\d+)\\.");
	QString temp = text;
	temp.remove(regExp);

	QStringList split = temp.split(QRegExp("\\r\\n|\\n"));

	for (int i = 0; i < split.size(); i++) {
		QString s = split.at(i);
		if (!s.isEmpty())
			m_output->insertPlainText(s + "\n");
	}
	QScrollBar *scrollBar = m_output->verticalScrollBar();
	scrollBar->setValue(scrollBar->maximum());
}

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

void SVNUpdateDialog::onUpdateToRevisionButtonClicked()
{
	m_textLabel->hide();
	m_cancelButton->hide();
	m_updateButton->hide();
	m_dateChooserWidget->hide();
	m_output->show();

	QStringList args;
	args << "update";

	// Pay attention: I have to perform update of the whole selected files
	// (that could become updatable "looking in the past")
	int filesCount = m_files.count();
	for (int i = 0; i < filesCount; i++) {
		QString file = m_files.at(i);
		if (!file.isEmpty())
			args << file;
	}
	QString revisionString = m_dateChooserWidget->getRevisionString();

	args << "-r" << revisionString;

	connect(&m_thread, SIGNAL(outputRetrieved(const QString &)), SLOT(addOutputText(const QString &)));
	connect(&m_thread, SIGNAL(done(const QString &)), SLOT(onUpdateDone(const QString &)));
	m_thread.executeCommand(m_workingDir, "svn", args, false);
}

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

void SVNUpdateDialog::onUpdateButtonClicked()
{
	// Update to mine
	QStringList files = m_conflictWidget->getFilesWithOption(0);
	if (files.size() > 0) {
		m_waitingLabel->show();
		m_textLabel->setText(tr("Updating items..."));
		QStringList args;
		args << "update";
		args.append(files);
		args << "--accept"
			 << "mine-full";

		m_thread.disconnect(SIGNAL(done(const QString &)));
		connect(&m_thread, SIGNAL(done(const QString &)), SLOT(onUpdateToMineDone()));
		m_thread.executeCommand(m_workingDir, "svn", args);
	} else
		onUpdateToMineDone();
}

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

void SVNUpdateDialog::onUpdateToMineDone()
{
	// Update to theirs
	QStringList files = m_conflictWidget->getFilesWithOption(1);
	if (files.size() > 0) {
		m_waitingLabel->show();
		m_textLabel->setText(tr("Updating to their items..."));
		QStringList args;
		args << "update";
		args.append(files);
		args << "--accept"
			 << "theirs-full";

		m_thread.disconnect(SIGNAL(done(const QString &)));
		connect(&m_thread, SIGNAL(done(const QString &)), SLOT(onConflictResolved()));
		m_thread.executeCommand(m_workingDir, "svn", args);
	} else
		onConflictResolved();
}

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

void SVNUpdateDialog::onConflictResolved()
{
	m_conflictWidget->hide();
	updateFiles();
}

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

void SVNUpdateDialog::onError(const QString &errorString)
{
	m_waitingLabel->hide();
	if (m_output->isVisible())
		addOutputText(errorString);
	else {
		m_textLabel->setText(errorString);
		m_textLabel->show();
	}
	switchToCloseButton();
}

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

void SVNUpdateDialog::onUpdateDone(const QString &text)
{
	addOutputText(text);

	QStringList files;
	for (int i = 0; i < m_filesToUpdate.size(); i++)
		files.append(m_filesToUpdate.at(i));
	emit done(files);

	switchToCloseButton();
}

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

void SVNUpdateDialog::onConflictSetted()
{
	m_updateButton->setEnabled(true);
}

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

void SVNUpdateDialog::onUpdateSceneContentsToggled(bool checked)
{
	if (!checked)
		m_sceneResources.clear();
	else {
		VersionControl *vc = VersionControl::instance();

		int fileSize = m_filesToUpdate.count();
		for (int i = 0; i < fileSize; i++) {
			QString fileName = m_filesToUpdate.at(i);
			if (fileName.endsWith(".tnz"))
				m_sceneResources.append(vc->getSceneContents(m_workingDir, fileName));
		}
	}

	if (m_filesToUpdate.size() == 1)
		m_textLabel->setText(tr("%1 items to update.").arg(m_filesToUpdate.size() + m_sceneResources.size()));
	else
		m_textLabel->setText(tr("%1 items to update.").arg(m_filesToUpdate.size() + m_sceneResources.size() - m_sceneIconsCount));
}