Blob Blame History Raw


#include "versioncontrol.h"
#include "versioncontrolgui.h"

#include "toonzqt/gutil.h"
#include "toonzqt/dvdialog.h"
#include "toonzqt/icongenerator.h"
#include "toonz/sceneresources.h"
#include "toonz/toonzscene.h"
#include "toonz/levelset.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/txshleveltypes.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/palettecontroller.h"
#include "toonz/tpalettehandle.h"
#include "toonz/tscenehandle.h"
#include "tsystem.h"
#include "tenv.h"
#include "tapp.h"

#include <QFile>
#include <QDir>
#include <QSettings>
#include <QRegExp>

namespace
{

bool getFrameRange(const TFilePath &path, unsigned int &from, unsigned int &to)
{
	// Set frame range
	if (path.getDots() == "..") {
		TFilePath dir = path.getParentDir();
		QDir qDir(QString::fromStdWString(dir.getWideString()));
		QString levelName = QRegExp::escape(QString::fromStdWString(path.getWideName()));
		QString levelType = QString::fromStdString(path.getType());
		QString exp(levelName + ".[0-9]{1,4}." + levelType);
		QRegExp regExp(exp);
		QStringList list = qDir.entryList(QDir::Files);
		QStringList levelFrames = list.filter(regExp);

		from = -1;
		to = -1;
		for (int i = 0; i < levelFrames.size(); i++) {
			TFilePath frame = dir + TFilePath(levelFrames[i].toStdWString());
			if (frame.isEmpty() || !frame.isAbsolute())
				continue;
			TFileStatus filestatus(frame);
			if (filestatus.isWritable()) {
				if (from == -1)
					from = i;
				else
					to = i;
			} else if (from != -1 && to != -1)
				break;
		}

		if (from != -1 && to != -1)
			return true;
		else
			return false;
	}
	return false;
}

} // namespace

//=============================================================================
// VersionControlThread
//-----------------------------------------------------------------------------

VersionControlThread::VersionControlThread(QObject *parent)
	: QThread(parent), m_abort(false), m_restart(false), m_getStatus(false), m_readOutputOnDone(true), m_process(0)
{
}

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

VersionControlThread::~VersionControlThread()
{
	m_mutex.lock();
	m_abort = true;
	m_condition.wakeOne();
	m_mutex.unlock();

	wait();
}

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

void VersionControlThread::run()
{
	forever
	{
		m_mutex.lock();
		QString workingDir = m_workingDir;
		QString binary = m_binary;
		QStringList args = m_args;

		// Add Username and Password (if exists)

		VersionControl *vc = VersionControl::instance();

		QString userName = vc->getUserName();
		QString password = vc->getPassword();

		if (!userName.isEmpty() || !password.isEmpty()) {
			args << QString("--username") << userName << QString("--password") << password;
		}

		// Add "non-interactive" and "trust-server-cert" global options
		args << "--non-interactive";
		args << "--trust-server-cert";

		QString executablePath = vc->getExecutablePath();
		if (!executablePath.isEmpty())
			binary = executablePath + "/" + m_binary;

		m_mutex.unlock();

		if (m_abort) {
			if (m_process) {
				m_process->close();
				disconnect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStandardOutputReady()));
				delete m_process;
				m_process = 0;
			}
			return;
		}

		if (m_process) {
			m_process->close();
			disconnect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStandardOutputReady()));
			delete m_process;
			m_process = 0;
		}

		m_process = new QProcess;
		if (!workingDir.isEmpty() && QFile::exists(workingDir))
			m_process->setWorkingDirectory(workingDir);

		QStringList env = QProcess::systemEnvironment();
		env << "LC_MESSAGES=en_EN"; // Add this environment variables to fix the language to English
		m_process->setEnvironment(env);

		if (!m_readOutputOnDone)
			connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(onStandardOutputReady()));

		m_process->start(binary, args);
		if (!m_process->waitForStarted()) {
			QString err = QString("Unable to launch \"%1\": %2").arg(binary, m_process->errorString());
			emit error(err);
			return;
		}

		m_process->closeWriteChannel();

		// Wait until users press cancel (every 1 sec I check for abort..
		while (!m_process->waitForFinished(1000)) {
			if (m_abort) {
				m_process->kill();
				m_process->waitForFinished();
				return;
			}
		}

		if (m_process->exitStatus() != QProcess::NormalExit) {
			QString err = QString("\"%1\" crashed.").arg(binary);
			emit error(err);
			return;
		}

		if (m_process->exitCode()) {
			emit error(QString::fromUtf8(m_process->readAllStandardError().data()));
			return;
		}

		if (m_getStatus) {
			emit statusRetrieved(QString::fromUtf8(m_process->readAllStandardOutput().data()));
			m_getStatus = false;
		} else
			emit done(QString::fromUtf8(m_process->readAllStandardOutput().data()));

		m_mutex.lock();
		if (!m_restart)
			m_condition.wait(&m_mutex);
		m_restart = false;
		m_mutex.unlock();
	}
}

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

void VersionControlThread::onStandardOutputReady()
{
	QString str = QString::fromUtf8(m_process->readAllStandardOutput().data());
	if (str.isEmpty())
		return;
	emit outputRetrieved(str);
}

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

void VersionControlThread::executeCommand(const QString &workingDir,
										  const QString &binary,
										  const QStringList &args,
										  bool readOutputOnDone)
{
	QMutexLocker locker(&m_mutex);

	m_readOutputOnDone = readOutputOnDone;
	m_workingDir = workingDir;
	m_binary = binary;
	m_args = args;

	if (m_binary.isEmpty())
		return;

	if (!isRunning()) {
		start(QThread::NormalPriority);
	} else {
		m_restart = true;
		m_condition.wakeOne();
	}
}

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

void VersionControlThread::getSVNStatus(const QString &path,
										bool showUpdates, bool nonRecursive, bool depthInfinity)
{
	QMutexLocker locker(&m_mutex);

	m_workingDir = path;
	m_binary = QString("svn");
	QStringList args;
	args << "status";
	if (showUpdates)
		args << "-vu";
	else
		args << "-v";
	if (nonRecursive)
		args << "--non-recursive";
	if (depthInfinity)
		args << "--depth"
			 << "infinity";

	args << "--xml";
	m_args = args;

	m_getStatus = true;
	m_readOutputOnDone = true;

	if (!isRunning()) {
		start(QThread::NormalPriority);
	} else {
		m_restart = true;
		m_condition.wakeOne();
	}
}

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

void VersionControlThread::getSVNStatus(const QString &path,
										const QStringList &files, bool showUpdates, bool nonRecursive, bool depthInfinity)
{
	QMutexLocker locker(&m_mutex);
	m_workingDir = path;
	m_binary = QString("svn");
	QStringList args;
	args << "status";
	if (showUpdates)
		args << "-vu";
	else
		args << "-v";

	int filesCount = files.size();
	for (int i = 0; i < filesCount; i++) {
		QString f = files.at(i);
		if (!f.isEmpty())
			args << f;
	}
	if (nonRecursive)
		args << "--non-recursive";
	if (depthInfinity)
		args << "--depth"
			 << "infinity";

	args << "--xml";
	m_args = args;

	m_getStatus = true;
	m_readOutputOnDone = true;

	if (!isRunning()) {
		start(QThread::NormalPriority);
	} else {
		m_restart = true;
		m_condition.wakeOne();
	}
}

//=============================================================================
// VersionControlManager
//-----------------------------------------------------------------------------

VersionControlManager::VersionControlManager()
	: m_scene(0), m_levelSet(0), m_isRunning(false), m_deleteLater(false)
{
	connect(&m_thread, SIGNAL(error(const QString &)), this, SLOT(onError(const QString &)));
}

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

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

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

void VersionControlManager::setFrameRange(TLevelSet *levelSet, bool deleteLater)
{
	if (levelSet->getLevelCount() == 0)
		return;

	if (!m_isRunning) {
		m_scene = 0; // Just to be sure
		m_levelSet = levelSet;
		m_deleteLater = deleteLater;

		QStringList args;
		args << "propget";
		args << "partial-lock";

		bool checkVersionControl = false;
		bool filesAddedToArgs = false;

		for (int i = 0; i < levelSet->getLevelCount(); i++) {
			TXshLevel *level = levelSet->getLevel(i);
			TXshSimpleLevel *sl = level->getSimpleLevel();

			if (sl && !checkVersionControl) {
				TFilePath parentDir = sl->getScene()->decodeFilePath(sl->getPath().getParentDir());
				if (VersionControl::instance()->isFolderUnderVersionControl(toQString(parentDir)))
					checkVersionControl = true;
			}

			if (sl && sl->isReadOnly()) {
				if (!m_scene)
					m_scene = sl->getScene();

				if (sl->getType() == PLI_XSHLEVEL || sl->getType() == TZP_XSHLEVEL) {
					filesAddedToArgs = true;
					args << toQString(m_scene->decodeFilePath(level->getPath()));
				} else if (sl->getType() == OVL_XSHLEVEL) {
					unsigned int from;
					unsigned int to;
					bool ret = getFrameRange(m_scene->decodeFilePath(level->getPath()), from, to);
					if (ret)
						sl->setEditableRange(from, to, VersionControl::instance()->getUserName().toStdWString());
				}
			}
		}

		if (!checkVersionControl || !filesAddedToArgs) {
			if (m_deleteLater) {
				m_deleteLater = false;
				for (int i = m_levelSet->getLevelCount() - 1; i >= 0; i--) {
					TXshLevel *l = m_levelSet->getLevel(i);
					m_levelSet->removeLevel(l);
				}
				delete m_levelSet;
				m_levelSet = 0;
			}
			m_scene = 0;
			return;
		}

		args << "--xml";

		TFilePath path = m_scene->getScenePath();
		path = m_scene->decodeFilePath(path);
		QString workingDir = toQString(path.getParentDir());

		m_thread.disconnect(SIGNAL(done(const QString &)));
		connect(&m_thread, SIGNAL(done(const QString &)), this, SLOT(onFrameRangeDone(const QString)));
		m_thread.executeCommand(workingDir, "svn", args, true);
		m_isRunning = true;
	}
}

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

void VersionControlManager::onFrameRangeDone(const QString &text)
{
	m_isRunning = false;

	SVNPartialLockReader lockReader(text);
	QList<SVNPartialLock> list = lockReader.getPartialLock();
	if (list.isEmpty()) {
		if (m_deleteLater) {
			m_deleteLater = false;

			for (int i = m_levelSet->getLevelCount() - 1; i >= 0; i--) {
				TXshLevel *l = m_levelSet->getLevel(i);
				TXshSimpleLevel *sl = l->getSimpleLevel();
				if (sl)
					sl->clearEditableRange();
				m_levelSet->removeLevel(l);
			}

			delete m_levelSet;
			m_levelSet = 0;
		}
		m_scene = 0;
		return;
	}

	int listSize = list.count();
	for (int l = 0; l < listSize; l++) {
		SVNPartialLock pl = list.at(l);

		QList<SVNPartialLockInfo> lockInfos = pl.m_partialLockList;

		TFilePath currentPath = TFilePath(pl.m_fileName.toStdWString());

		TXshSimpleLevel *level;
		for (int i = 0; i < m_levelSet->getLevelCount(); i++) {
			TXshLevel *l = m_levelSet->getLevel(i);
			TFilePath levelPath = m_scene->decodeFilePath(l->getPath());
			if (levelPath == currentPath) {
				level = l->getSimpleLevel();
				break;
			}
		}

		QString username = VersionControl::instance()->getUserName();
		QString hostName = TSystem::getHostName();

		int count = lockInfos.size();
		for (int i = 0; i < count; i++) {
			SVNPartialLockInfo info = lockInfos.at(i);
			if (info.m_userName == username && info.m_hostName == hostName) {
				level->setEditableRange(info.m_from - 1, info.m_to - 1, info.m_userName.toStdWString());
				invalidateIcons(level, level->getEditableRange());
				TApp *app = TApp::instance();
				if (app->getCurrentLevel()->getLevel() == level) {
					app->getPaletteController()->getCurrentLevelPalette()->setPalette(level->getPalette());
					app->getCurrentLevel()->notifyLevelChange();
				}
				break;
			}
		}
	}

	if (m_deleteLater) {
		m_deleteLater = false;

		for (int i = m_levelSet->getLevelCount() - 1; i >= 0; i--) {
			TXshLevel *l = m_levelSet->getLevel(i);
			m_levelSet->removeLevel(l);
		}

		delete m_levelSet;
		m_levelSet = 0;
	}
	m_scene = 0;
}

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

void VersionControlManager::onError(const QString &text)
{
	m_isRunning = false;
	if (m_deleteLater) {
		m_deleteLater = false;
		for (int i = m_levelSet->getLevelCount() - 1; i >= 0; i--) {
			TXshLevel *l = m_levelSet->getLevel(i);
			m_levelSet->removeLevel(l);
		}
		delete m_levelSet;
		m_levelSet = 0;
	}
	m_scene = 0;
	DVGui::MsgBox(WARNING, text);
}

//=============================================================================
// VersionControl
//-----------------------------------------------------------------------------

VersionControl::VersionControl()
	: m_userName(), m_password(), m_executablePath()
{
}

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

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

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

void VersionControl::init()
{
	QString configFileName = QString::fromStdWString(TFilePath(TEnv::getConfigDir() + "versioncontrol.xml").getWideString());

	if (QFile::exists(configFileName)) {
		QFile file(configFileName);
		if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
			QString content = QString(file.readAll());
			SVNConfigReader reader(content);
			m_repositories = reader.getRepositories();
			m_executablePath = reader.getSVNPath();
		}
	}
}

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

bool VersionControl::testSetup()
{
	QString configFileName = QString::fromStdWString(TFilePath(TEnv::getConfigDir() + "versioncontrol.xml").getWideString());

	int repositoriesCount = 0;
	QString path;
	if (QFile::exists(configFileName)) {
		QFile file(configFileName);
		if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
			QString content = QString(file.readAll());
			SVNConfigReader reader(content);
			repositoriesCount = reader.getRepositories().size();
			path = reader.getSVNPath();
		}
	}

	// Test configuration file
	if (repositoriesCount == 0) {
		MsgBox(CRITICAL, tr("The version control configuration file is empty or wrongly defined.\nPlease refer to the user guide for details."));
		return false;
	}

// Test if svnPath is correct (adding another "/" before binary name doesn't create any issue both on Windows and on Mac
#ifdef MACOSX
	if (!path.isEmpty() && !QFile::exists(path + "/svn"))
#else
	if (!path.isEmpty() && !QFile::exists(path + "/svn.exe"))
#endif
	{
		MsgBox(CRITICAL, tr("The version control client application specified on the configuration file cannot be found.\nPlease refer to the user guide for details."));
		return false;
	}

	// Try to run svn executable, if the path is not specified in the config file,
	// to test if exist
	// In the meantime check for svn version
	if (path.isEmpty()) {
		QProcess p;

		QStringList env = QProcess::systemEnvironment();
		env << "LC_MESSAGES=en_EN"; // Add this environment variables to fix the language to English
		p.setEnvironment(env);

		p.start("svn", QStringList("--version"));

		if (!p.waitForStarted()) {
			MsgBox(CRITICAL, tr("The version control client application is not installed on your computer.\nSubversion 1.5 or later is required.\nPlease refer to the user guide for details."));
			return false;
		}

		if (!p.waitForFinished()) {
			MsgBox(CRITICAL, tr("The version control client application is not installed on your computer.\nSubversion 1.5 or later is required.\nPlease refer to the user guide for details."));
			return false;
		}

		QString output(p.readAllStandardOutput());

		QStringList list = output.split("\n");

		if (!list.isEmpty()) {
			QString firstLine = list.first();
			firstLine = firstLine.remove("svn, version ");

			double version = firstLine.left(3).toDouble();
			if (version <= 1.5) {
				MsgBox(WARNING, tr("The version control client application installed on your computer needs to be updated, otherwise some features may not be available.\nSubversion 1.5 or later is required.\nPlease refer to the user guide for details."));
				return true;
			}
		}
	}
	return true;
}

//-----------------------------------------------------------------------------
bool VersionControl::isFolderUnderVersionControl(const QString &folderPath)
{
	QDir dir(folderPath);
	return dir.entryList(QDir::AllDirs | QDir::Hidden).contains(".svn");
}

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

void VersionControl::commit(QWidget *parent, const QString &workingDir,
							const QStringList &filesToCommit, bool folderOnly,
							int sceneIconAdded)
{
	SVNCommitDialog *dialog = new SVNCommitDialog(parent, workingDir, filesToCommit, folderOnly, sceneIconAdded);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::revert(QWidget *parent, const QString &workingDir,
							const QStringList &files, bool folderOnly,
							int sceneIconAdded)
{
	SVNRevertDialog *dialog = new SVNRevertDialog(parent, workingDir, files, folderOnly, sceneIconAdded);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::update(QWidget *parent, const QString &workingDir,
							const QStringList &filesToUpdate, int sceneIconsCounts,
							bool folderOnly, bool updateToRevision, bool nonRecursive)
{
	SVNUpdateDialog *dialog = new SVNUpdateDialog(parent, workingDir, filesToUpdate, sceneIconsCounts, folderOnly, updateToRevision, nonRecursive);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::updateAndLock(QWidget *parent, const QString &workingDir,
								   const QStringList &files, int workingRevision, int sceneIconAdded)
{
	SVNUpdateAndLockDialog *dialog = new SVNUpdateAndLockDialog(parent, workingDir, files, workingRevision, sceneIconAdded);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::lock(QWidget *parent, const QString &workingDir,
						  const QStringList &filesToLock, int sceneIconAdded)
{
	SVNLockDialog *dialog = new SVNLockDialog(parent, workingDir, filesToLock, true, sceneIconAdded);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::unlock(QWidget *parent, const QString &workingDir,
							const QStringList &filesToUnlock, int sceneIconAdded)
{
	SVNLockDialog *dialog = new SVNLockDialog(parent, workingDir, filesToUnlock, false, sceneIconAdded);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::lockFrameRange(QWidget *parent, const QString &workingDir,
									const QString &file, int frameCount)
{
	SVNLockFrameRangeDialog *dialog = new SVNLockFrameRangeDialog(parent, workingDir, file, frameCount);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::lockFrameRange(QWidget *parent, const QString &workingDir,
									const QStringList &files)
{
	SVNLockMultiFrameRangeDialog *dialog = new SVNLockMultiFrameRangeDialog(parent, workingDir, files);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::unlockFrameRange(QWidget *parent, const QString &workingDir, const QString &file)
{
	SVNUnlockFrameRangeDialog *dialog = new SVNUnlockFrameRangeDialog(parent, workingDir, file);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::unlockFrameRange(QWidget *parent, const QString &workingDir, const QStringList &files)
{
	SVNUnlockMultiFrameRangeDialog *dialog = new SVNUnlockMultiFrameRangeDialog(parent, workingDir, files);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::showFrameRangeLockInfo(QWidget *parent, const QString &workingDir,
											const QString &file)
{
	SVNFrameRangeLockInfoDialog *dialog = new SVNFrameRangeLockInfoDialog(parent, workingDir, file);
	dialog->show();
	dialog->raise();
}

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

void VersionControl::showFrameRangeLockInfo(QWidget *parent, const QString &workingDir,
											const QStringList &files)
{
	SVNMultiFrameRangeLockInfoDialog *dialog = new SVNMultiFrameRangeLockInfoDialog(parent, workingDir, files);
	dialog->show();
	dialog->raise();
}

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

void VersionControl::commitFrameRange(QWidget *parent, const QString &workingDir, const QString &file)
{
	SVNCommitFrameRangeDialog *dialog = new SVNCommitFrameRangeDialog(parent, workingDir, file);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::revertFrameRange(QWidget *parent, const QString &workingDir, const QString &file,
									  const QString &tempFileName)
{
	SVNRevertFrameRangeDialog *dialog = new SVNRevertFrameRangeDialog(parent, workingDir, file, tempFileName);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::deleteFiles(QWidget *parent,
								 const QString &workingDir,
								 const QStringList &filesToDelete,
								 int sceneIconAdded)
{
	SVNDeleteDialog *dialog = new SVNDeleteDialog(parent, workingDir, filesToDelete, false, sceneIconAdded);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::deleteFolder(QWidget *parent,
								  const QString &workingDir,
								  const QString &folderName)
{
	SVNDeleteDialog *dialog = new SVNDeleteDialog(parent, workingDir, QStringList(folderName), true, 0);
	connect(dialog, SIGNAL(done(const QStringList &)), this, SIGNAL(commandDone(const QStringList &)));
	dialog->show();
	dialog->raise();
}

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

void VersionControl::cleanupFolder(QWidget *parent, const QString &workingDir)
{
	SVNCleanupDialog *dialog = new SVNCleanupDialog(parent, workingDir);
	dialog->show();
	dialog->raise();
}

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

void VersionControl::purgeFolder(QWidget *parent, const QString &workingDir)
{
	SVNPurgeDialog *dialog = new SVNPurgeDialog(parent, workingDir);
	dialog->show();
	dialog->raise();
}

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

QStringList VersionControl::getSceneContents(const QString &wokingDir,
											 const QString &sceneFileName)
{
	QStringList sceneContents;

	TFilePath scenePath = TFilePath(wokingDir.toStdWString()) + sceneFileName.toStdWString();
	if (!TFileStatus(scenePath).doesExist())
		return sceneContents;

	ToonzScene scene;
	try {
		scene.load(scenePath);
	} catch (...) {
	}
	std::vector<TXshLevel *> levels;
	scene.getLevelSet()->listLevels(levels);
	std::vector<TXshLevel *>::iterator it;
	for (it = levels.begin(); it != levels.end(); ++it) {
		TFilePath levelPath = scene.decodeFilePath((*it)->getPath());

		if (levelPath.getDots() == "..") {
			TFilePath dir = levelPath.getParentDir();
			QDir qDir(QString::fromStdWString(dir.getWideString()));
			QString levelName = QRegExp::escape(QString::fromStdWString(levelPath.getWideName()));
			QString levelType = QString::fromStdString(levelPath.getType());
			QString exp(levelName + ".[0-9]{1,4}." + levelType);
			QRegExp regExp(exp);
			QStringList list = qDir.entryList(QDir::Files);
			list = list.filter(regExp);
			for (int i = 0; i < list.size(); i++) {
				QString fileName = list.at(i);
				sceneContents.append(toQString(dir + fileName.toStdWString()));
			}
		} else
			sceneContents.append(toQString(levelPath));

		TFilePathSet fpset;
		TXshSimpleLevel::getFiles(levelPath, fpset);

		TFilePathSet::iterator it;
		for (it = fpset.begin(); it != fpset.end(); ++it)
			sceneContents.append(toQString(scene.decodeFilePath((*it))));
	}

	return sceneContents;
}

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

QStringList VersionControl::getCurrentSceneContents() const
{
	QStringList contents;
	ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
	if (!scene)
		return QStringList();

	std::vector<TXshLevel *> levels;
	scene->getLevelSet()->listLevels(levels);
	std::vector<TXshLevel *>::iterator it;
	for (it = levels.begin(); it != levels.end(); ++it) {
		TFilePath levelPath = scene->decodeFilePath((*it)->getPath());
		contents.append(toQString(levelPath));

		TFilePathSet fpset;
		TXshSimpleLevel::getFiles(levelPath, fpset);

		TFilePathSet::iterator it;
		for (it = fpset.begin(); it != fpset.end(); ++it)
			contents.append(toQString(scene->decodeFilePath((*it))));
	}
	return contents;
}