Blob Blame Raw


#include "filebrowsermodel.h"
#include "tutil.h"
#include "tsystem.h"
#include "tconvert.h"
#include "tenv.h"
#include "toonz/tproject.h"
#include "toonz/toonzscene.h"
#include "toonzqt/gutil.h"

#include "filebrowser.h"
#include "history.h"
#include "iocommand.h"

#include <QFileInfo>
#include <QDir>

#ifdef WIN32
#include <shlobj.h>
#include <Winnetwk.h>
#endif
#ifdef MACOSX
#include <Cocoa/Cocoa.h>
#endif

#ifdef LINETEST
#include "tnzcamera.h"
#endif

namespace
{
TFilePath getMyDocumentsPath()
{
#ifdef WIN32
	WCHAR szPath[MAX_PATH];
	if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_PERSONAL, 0)) {
		return TFilePath(szPath);
	}
	return TFilePath();
#elif defined MACOSX
	NSArray *foundref = NSSearchPathForDirectoriesInDomains(
		NSDocumentDirectory,
		NSUserDomainMask,
		YES);
	if (!foundref)
		return TFilePath();
	int c = [foundref count];
	assert(c == 1);
	NSString *documentsDirectory = [foundref objectAtIndex:0];
	return TFilePath((const char *)[documentsDirectory cStringUsingEncoding:NSASCIIStringEncoding]);
#else
	return TFilePath();
#endif
}
}

//=============================================================================
//
// DvDirModelNode
//
//-----------------------------------------------------------------------------

DvDirModelNode::DvDirModelNode(DvDirModelNode *parent, wstring name)
	: m_parent(parent), m_name(name), m_oldName(), m_row(-1), m_childrenValid(false), m_renameEnabled(false), m_nodeType("")
{
}

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

DvDirModelNode::~DvDirModelNode()
{
	clearPointerContainer(m_children);
}

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

void DvDirModelNode::removeChildren(int row, int count)
{
	assert(0 <= row && row + count <= (int)m_children.size());
	int i;
	for (i = 0; i < count; i++)
		delete m_children[row + i];
	m_children.erase(m_children.begin() + row, m_children.begin() + row + count);
	for (i = 0; i < (int)m_children.size(); i++)
		m_children[i]->setRow(i);
}

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

void DvDirModelNode::addChild(DvDirModelNode *child)
{
	child->setRow(m_children.size());
	m_children.push_back(child);
}

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

int DvDirModelNode::getChildCount()
{
	if (!m_childrenValid)
		refreshChildren();
	return (int)m_children.size();
}

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

DvDirModelNode *DvDirModelNode::getChild(int index)
{
	if (!m_childrenValid)
		refreshChildren();
	assert(0 <= index && index < getChildCount());
	return m_children[index];
}

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

bool DvDirModelNode::hasChildren()
{
	if (m_childrenValid)
		return getChildCount() > 0;
	else
		return true;
}

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

QPixmap DvDirModelNode::getPixmap(bool isOpen) const
{
	return QPixmap();
}

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

void DvDirModelNode::setTemporaryName(const wstring &newName)
{
	m_oldName = getName();
	m_name = newName;
}

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

void DvDirModelNode::restoreName()
{
	if (m_oldName != L"") {
		m_name = m_oldName;
		m_oldName = L"";
	}
}

//=============================================================================
//
// DvDirModelFileFolderNode [File folder]
//
//-----------------------------------------------------------------------------

DvDirModelFileFolderNode::DvDirModelFileFolderNode(DvDirModelNode *parent, wstring name, const TFilePath &path)
	: DvDirModelNode(parent, name), m_path(path), m_isProjectFolder(false), m_existsChecked(false), m_exists(true), m_hasChildren(false), m_peeks(true)
{
	m_nodeType = "FileFolder";
}

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

DvDirModelFileFolderNode::DvDirModelFileFolderNode(DvDirModelNode *parent, const TFilePath &path)
	: DvDirModelNode(parent, path.withoutParentDir().getWideString()), m_path(path), m_isProjectFolder(false), m_existsChecked(false), m_exists(true), m_hasChildren(false), m_peeks(true)
{
	m_nodeType = "FileFolder";
}

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

bool DvDirModelFileFolderNode::exists()
{
	return m_existsChecked ? m_exists : m_peeks ? m_existsChecked = true, m_exists = TFileStatus(m_path).doesExist() : true;
}

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

void DvDirModelFileFolderNode::visualizeContent(FileBrowser *browser)
{
	browser->setFolder(m_path);
}

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

DvDirModelNode *DvDirModelFileFolderNode::makeChild(wstring name)
{
	return createNode(this, m_path + name);
}

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

DvDirModelFileFolderNode *DvDirModelFileFolderNode::createNode(DvDirModelNode *parent, const TFilePath &path)
{
	DvDirModelFileFolderNode *node;
	// check the project nodes under the Poject Root Node
	if (QString::fromStdWString(parent->getName()).startsWith("Project root") && TProjectManager::instance()->isProject(path))
		node = new DvDirModelProjectNode(parent, path);
	else {
		node = new DvDirModelFileFolderNode(parent, path);
		if (path.getName().find("_files") == string::npos)
			node->enableRename(true);
	}
	return node;
}

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

bool DvDirModelFileFolderNode::setName(wstring newName)
{
	if (isSpaceString(QString::fromStdWString(newName)))
		return true;

	TFilePath srcPath = getPath();
	TFilePath dstPath = getPath().getParentDir() + newName;
	if (srcPath == dstPath)
		return false;
	try {
		TSystem::renameFile(dstPath, srcPath);
	} catch (...) {
		return false;
	}
	m_path = dstPath;
	m_name = newName;
	return true;
}

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

bool DvDirModelFileFolderNode::hasChildren()
{
	if (m_childrenValid)
		return m_hasChildren;

	if (m_peeks) {
		// Using QDir directly rather than DvDirModelFileFolderNode::refreshChildren() due to
		// performance issues
		QDir dir(QString::fromStdWString(m_path.getWideString()));
		dir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);

		return (dir.count() > 0);
	} else
		return true; // Not peeking nodes allow users to actively scan for sub-folders
}

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

void DvDirModelFileFolderNode::refreshChildren()
{
	m_childrenValid = true;

	std::vector<wstring> names;
	getChildrenNames(names);

	std::vector<DvDirModelNode *> oldChildren;
	oldChildren.swap(m_children);

	std::vector<DvDirModelNode *>::iterator j;
	int i;
	for (i = 0; i < (int)names.size(); i++) {
		wstring name = names[i];
		for (j = oldChildren.begin(); j != oldChildren.end() && (*j)->getName() != name; ++j) {
		}
		DvDirModelNode *child = 0;
		if (j != oldChildren.end()) {
			child = *j;
			oldChildren.erase(j);
		} else {
			child = makeChild(name);
			if (DvDirModelFileFolderNode *folderNode = dynamic_cast<DvDirModelFileFolderNode *>(child))
				folderNode->setPeeking(m_peeks);
		}

		if (!child)
			continue;

		addChild(child);
	}
	for (j = oldChildren.begin(); j != oldChildren.end(); ++j) {
		DvDirModelNode *child = *j;
		if (!!child && child->hasChildren())
			child->removeChildren(0, child->getChildCount());

		delete *j;
	}

	m_hasChildren = (m_children.size() > 0);
}

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

void DvDirModelFileFolderNode::getChildrenNames(std::vector<wstring> &names) const
{
	TFileStatus folderPathStatus(m_path);
	if (folderPathStatus.isLink())
		return;

	QStringList entries;
	if (folderPathStatus.isDirectory()) {
		QDir dir(toQString(m_path));

		entries = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name | QDir::LocaleAware);
	}

	int e, eCount = entries.size();
	for (e = 0; e != eCount; ++e)
		names.push_back(entries[e].toStdWString());
}

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

int DvDirModelFileFolderNode::rowByName(const wstring &name) const
{
	std::vector<wstring> names;
	getChildrenNames(names);
	std::vector<wstring>::iterator it = std::find(names.begin(), names.end(), name);
	if (it == names.end())
		return -1;
	else
		return std::distance(names.begin(), it);
}

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

DvDirModelNode *DvDirModelFileFolderNode::getNodeByPath(const TFilePath &path)
{
	if (path == m_path)
		return this;
	if (!m_path.isAncestorOf(path))
		return 0;
	int i, n = getChildCount();
	for (i = 0; i < n; i++) {
		DvDirModelNode *node = getChild(i)->getNodeByPath(path);
		if (node)
			return node;
	}
	return 0;
}

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

QPixmap DvDirModelFileFolderNode::getPixmap(bool isOpen) const
{
	static QPixmap openFolderPixmap(":Resources/browser_folder_open.png");
	static QPixmap closeFolderPixmap(":Resources/browser_folder_close.png");
	return isOpen ? openFolderPixmap : closeFolderPixmap;
}

//=============================================================================
//
// DvDirModelSceneFolderNode
//
//-----------------------------------------------------------------------------

DvDirModelSceneFolderNode::DvDirModelSceneFolderNode(DvDirModelNode *parent, wstring name, const TFilePath &scenePath)
	: DvDirModelFileFolderNode(parent, name, scenePath)
{
}

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

DvDirModelSceneFolderNode::DvDirModelSceneFolderNode(DvDirModelNode *parent, const TFilePath &path)
	: DvDirModelFileFolderNode(parent, path.withoutParentDir().getWideString(), path)
{
	try {
		ToonzScene scene;
		scene.setScenePath(path);
		TProjectP project = TProjectManager::instance()->loadSceneProject(path);
		if (!project)
			return;
		scene.setProject(project.getPointer());
		for (int i = 0; i < project->getFolderCount(); i++) {
			string folderName = project->getFolderName(i);
			TFilePath folderPath = project->getFolder(i);
			if (folderPath.isAbsolute() && !project->isConstantFolder(i)) {
				wstring alias = L"+" + toWideString(folderName);
				TFilePath folderActualPath = scene.decodeFilePath(TFilePath(alias));
				m_folders[alias] = folderActualPath;
			}
		}
	} catch (...) {
	}
}

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

DvDirModelSceneFolderNode::~DvDirModelSceneFolderNode()
{
}

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

bool DvDirModelSceneFolderNode::setName(wstring newName)
{
	m_name = newName;
	return true;
}

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

QPixmap DvDirModelSceneFolderNode::getPixmap(bool isOpen) const
{
	static QPixmap openFolderPixmap(":Resources/browser_scene_open.png");
	static QPixmap closeFolderPixmap(":Resources/browser_scene_close.png");
	return isOpen ? openFolderPixmap : closeFolderPixmap;
}

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

DvDirModelNode *DvDirModelSceneFolderNode::makeChild(wstring name)
{
	TFilePath actualFolderPath = m_folders[name];
	DvDirModelFileFolderNode *node = new DvDirModelFileFolderNode(this, name, actualFolderPath);
	node->enableRename(false);
	return node;
}

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

void DvDirModelSceneFolderNode::getChildrenNames(std::vector<wstring> &names) const
{
	std::map<wstring, TFilePath>::const_iterator it;
	for (it = m_folders.begin(); it != m_folders.end(); ++it)
		names.push_back(it->first);
}

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

void DvDirModelSceneFolderNode::refreshChildren()
{
	m_childrenValid = true;
	std::vector<wstring> names;
	getChildrenNames(names);

	std::vector<DvDirModelNode *> oldChildren;
	oldChildren.swap(m_children);

	std::vector<DvDirModelNode *>::iterator j;
	int i;
	for (i = 0; i < (int)names.size(); i++) {
		wstring name = names[i];
		for (j = oldChildren.begin(); j != oldChildren.end() && (*j)->getName() != name; ++j) {
		}
		DvDirModelNode *child = 0;
		if (j != oldChildren.end()) {
			child = *j;
			oldChildren.erase(j);
		} else {
			child = makeChild(name);
		}
		addChild(child);
	}
	for (j = oldChildren.begin(); j != oldChildren.end(); ++j) {
		DvDirModelNode *child = *j;
		if (!!child && child->hasChildren())
			child->removeChildren(0, child->getChildCount());
		delete *j;
	}
}

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

DvDirModelFileFolderNode *DvDirModelSceneFolderNode::createNode(DvDirModelNode *parent, const TFilePath &path)
{
	DvDirModelFileFolderNode *node = new DvDirModelFileFolderNode(parent, path);
	if (path.getName().find("_files") == string::npos)
		node->enableRename(true);
	return node;
}

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

int DvDirModelSceneFolderNode::rowByName(const wstring &name)
{
	std::vector<wstring> names;
	getChildrenNames(names);
	std::vector<wstring>::iterator it = std::find(names.begin(), names.end(), name);
	if (it == names.end())
		return -1;
	else
		return std::distance(names.begin(), it);
}

//=============================================================================
//
// DvDirModelSpecialFileFolderNode [Special File Folder]
//
//-----------------------------------------------------------------------------

DvDirModelSpecialFileFolderNode::DvDirModelSpecialFileFolderNode(
	DvDirModelNode *parent, wstring name, const TFilePath &path)
	: DvDirModelFileFolderNode(parent, name, path)
{
}

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

QPixmap DvDirModelSpecialFileFolderNode::getPixmap(bool isOpen) const
{
	return m_pixmap;
}

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

void DvDirModelSpecialFileFolderNode::setPixmap(const QPixmap &pixmap)
{
	m_pixmap = pixmap;
}

//=============================================================================
//
// DvDirVersionControlNode [Special File Folder]
//
//-----------------------------------------------------------------------------

DvDirVersionControlNode::DvDirVersionControlNode(DvDirModelNode *parent, wstring name, const TFilePath &path)
	: DvDirModelFileFolderNode(parent, name, path), m_isSynched(false), m_isUnversioned(false), m_oldName()
{
	this->setExists(TFileStatus(path).doesExist());
	setIsUnderVersionControl(VersionControl::instance()->isFolderUnderVersionControl(toQString(path)));
}

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

QList<TFilePath> DvDirVersionControlNode::getMissingFiles() const
{
	QList<TFilePath> list;
	QMap<QString, SVNStatus>::const_iterator i = m_statusMap.constBegin();
	while (i != m_statusMap.constEnd()) {
		SVNStatus s = i.value();
		if (s.m_item == "missing" || s.m_item == "none" && s.m_repoStatus == "added") {
			TFilePath path(getPath() + TFilePath(s.m_path.toStdWString()));
			std::string dots = path.getDots();
			if (dots != "") {
				if (dots == "..")
					path = path.getParentDir() + path.getLevelNameW();
				if (!list.contains(path))
					list.append(path);
			}
		}
		i++;
	}
	return list;
}

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

QStringList DvDirVersionControlNode::getMissingFiles(const QRegExp &filter) const
{
	QStringList list;
	QMap<QString, SVNStatus>::const_iterator i = m_statusMap.constBegin();
	for (; i != m_statusMap.constEnd(); i++) {
		SVNStatus s = i.value();
		if (s.m_item == "missing" || s.m_item == "none" && s.m_repoStatus == "added") {
			TFilePath path(s.m_path.toStdWString());
			if (!filter.exactMatch(QString::fromStdWString(path.withoutParentDir().getWideString())))
				continue;
			std::string dots = path.getDots();
			if (dots != "")
				list.append(toQString(path));
		}
	}
	return list;
}

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

QList<TFilePath> DvDirVersionControlNode::getMissingFolders() const
{
	QList<TFilePath> list;
	QMap<QString, SVNStatus>::const_iterator i = m_statusMap.constBegin();
	while (i != m_statusMap.constEnd()) {
		SVNStatus s = i.value();
		if (s.m_item == "missing" || (s.m_item == "none" && s.m_repoStatus == "added")) {
			TFilePath path(getPath() + TFilePath(s.m_path.toStdWString()));
			if (path.getDots() == "" || path.getType() == "tnz") {
				QString currentFolderName = QString::fromStdWString(getPath().getWideName());
				if (currentFolderName != s.m_path) {
					if (!s.m_path.contains("\\"))
						list.append(path);
					else {
						QString newPath = s.m_path.replace(currentFolderName + "\\", "");
						if (!newPath.contains("\\"))
							list.append(getPath() + TFilePath(newPath.toStdWString()));
					}
				}
			}
		}
		i++;
	}
	return list;
}

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

void DvDirVersionControlNode::getChildrenNames(std::vector<wstring> &names) const
{
	DvDirModelFileFolderNode::getChildrenNames(names);

	QList<TFilePath> missingFolders = getMissingFolders();
	int count = missingFolders.size();

	for (int i = 0; i < count; i++) {
		std::wstring name = missingFolders.at(i).getWideName();
		if (find(names.begin(), names.end(), name) == names.end())
			names.push_back(name);
	}
}

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

void DvDirVersionControlNode::setIsUnderVersionControl(bool value)
{
	// Set the flags and enable/disable renaming accordling
	m_isUnderVersionControl = value;
	enableRename(!value);
}

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

DvDirModelNode *DvDirVersionControlNode::makeChild(wstring name)
{
	if (TProjectManager::instance()->isProject(getPath() + name))
		return new DvDirVersionControlProjectNode(this, name, getPath() + name);
	return new DvDirVersionControlNode(this, name, getPath() + name);
}

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

QPixmap DvDirVersionControlNode::getPixmap(bool isOpen) const
{
	static QPixmap openFolderPixmap(":Resources/browser_folder_open.png");
	static QPixmap closeFolderPixmap(":Resources/browser_folder_close.png");
	static QPixmap openMissingPixmap(":Resources/vcfolder_mis_open.png");
	static QPixmap closeMissingPixmap(":Resources/vcfolder_mis_close.png");
	static QPixmap openSceneFolderPixmap(":Resources/browser_scene_open.png");
	static QPixmap closeSceneFolderPixmap(":Resources/browser_scene_close.png");

	if (TFileStatus(getPath()).doesExist()) {
		if (getPath().getType() == "tnz")
			return isOpen ? openSceneFolderPixmap : closeSceneFolderPixmap;
		else
			return isOpen ? openFolderPixmap : closeFolderPixmap;
	} else
		return isOpen ? openMissingPixmap : closeMissingPixmap;
}

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

void DvDirVersionControlNode::insertVersionControlStatus(const QString &fileName, SVNStatus status)
{
	m_statusMap.insert(fileName, status);
}

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

SVNStatus DvDirVersionControlNode::getVersionControlStatus(const QString &fileName)
{
	if (m_statusMap.contains(fileName))
		return m_statusMap.value(fileName);

	SVNStatus s = SVNStatus();
	s.m_isLocked = false;
	s.m_isPartialEdited = false;
	s.m_isPartialLocked = false;
	return s;
}

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

QStringList DvDirVersionControlNode::refreshVersionControl(const QList<SVNStatus> &status)
{
	TFilePath nodePath = getPath();
	if (nodePath.isEmpty())
		return QStringList();

	int listSize = status.size();
	bool isSynched = true;

	QStringList checkPartialLockList;

	for (int i = 0; i < listSize; i++) {
		SVNStatus s = status.at(i);

		// It is not needed to check and cache SVNStatus for files that are "outside" the node "scope"
		if (s.m_path == "." || s.m_path == ".." || s.m_path.split("\\").size() > 2)
			continue;

		QString fileName = s.m_path;
		if (nodePath.getType() == "")
			insertVersionControlStatus(fileName, s);

		// Update also the status of the "scene" child folders
		TFilePath path(fileName.toStdWString());
		if (path.getType() == "tnz") {
			DvDirVersionControlNode *childVcNode = dynamic_cast<DvDirVersionControlNode *>(getNodeByPath(nodePath + path));
			if (childVcNode) {
				childVcNode->setIsUnderVersionControl(true);
				if (s.m_repoStatus == "modified" || s.m_item == "modified" ||
					s.m_item == "missing" || s.m_item == "none")
					childVcNode->setIsSynched(false);
				else if (s.m_item == "unversioned")
					childVcNode->setIsUnversioned(true);
				else
					childVcNode->setIsSynched(true);
			}
		}
		// If a folder is unversioned, I set its status and even the unversioned status for its children
		else if (path.getType() == "" && s.m_item == "unversioned") {
			DvDirVersionControlNode *childVcNode = dynamic_cast<DvDirVersionControlNode *>(getNodeByPath(nodePath + path));
			if (childVcNode) {
				childVcNode->setIsUnversioned(true);
				int childCount = childVcNode->getChildCount();
				for (int i = 0; i < childCount; i++) {
					DvDirVersionControlNode *subChildNode = dynamic_cast<DvDirVersionControlNode *>(childVcNode->getChild(i));
					if (subChildNode)
						subChildNode->setIsUnversioned(true);
				}
			}
		}

		// Check also the 'partial-lock' property
		std::string type = TFilePath(s.m_path.toStdWString()).getType();
		if (type == "tlv" || type == "pli") {
			if (s.m_item != "unversioned" && s.m_item != "missing" && s.m_repoStatus != "added")
				checkPartialLockList.append(s.m_path);
		}

		if (s.m_repoStatus == "modified" || s.m_item == "modified" ||
			s.m_item == "unversioned" || s.m_item == "missing" || s.m_item == "none")
			isSynched = false;
	}

	setIsSynched(isSynched);
	return checkPartialLockList;
}

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

DvDirVersionControlRootNode *DvDirVersionControlNode::getVersionControlRootNode()
{
	DvDirModelNode *parent = getParent();
	if (parent)
		return parent->getVersionControlRootNode();
	return 0;
}

//=============================================================================
//
// DvDirVersionControlRootNode [Special File Folder]
//
//-----------------------------------------------------------------------------

DvDirVersionControlRootNode::DvDirVersionControlRootNode(DvDirModelNode *parent,
														 wstring name,
														 const TFilePath &path)
	: DvDirVersionControlNode(parent, name, path)
{
	setPixmap(QPixmap(":Resources/vcroot.png"));
	setIsUnderVersionControl(true);
}

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

void DvDirVersionControlRootNode::refreshChildren()
{
	DvDirModelFileFolderNode::refreshChildren();
}

//=============================================================================
// DvDirVersionControlProjectNode
//-----------------------------------------------------------------------------

DvDirVersionControlProjectNode::DvDirVersionControlProjectNode(
	DvDirModelNode *parent,
	wstring name,
	const TFilePath &path)
	: DvDirVersionControlNode(parent, name, path)
{
	m_nodeType = "Project";
	setIsUnderVersionControl(true);
}

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

TFilePath DvDirVersionControlProjectNode::getProjectPath() const
{
	return TProjectManager::instance()->projectFolderToProjectPath(getPath());
}

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

bool DvDirVersionControlProjectNode::isCurrent() const
{
	TProjectManager *pm = TProjectManager::instance();
	return pm->getCurrentProjectPath().getParentDir() == getPath();
}

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

void DvDirVersionControlProjectNode::makeCurrent()
{
	TProjectManager *pm = TProjectManager::instance();
	TFilePath projectPath = getProjectPath();
	if (!IoCmd::saveSceneIfNeeded(QObject::tr("Change project")))
		return;
	pm->setCurrentProjectPath(projectPath);
	IoCmd::newScene();
}

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

QPixmap DvDirVersionControlProjectNode::getPixmap(bool isOpen) const
{
	static QPixmap openPixmap(":Resources/browser_vcproject_open.png");
	static QPixmap closePixmap(":Resources/browser_vcproject_close.png");
	static QPixmap openMissingPixmap(":Resources/vcfolder_mis_open.png");
	static QPixmap closeMissingPixmap(":Resources/vcfolder_mis_close.png");

	if (TFileStatus(getPath()).doesExist())
		return isOpen ? openPixmap : closePixmap;
	else
		return isOpen ? openMissingPixmap : closeMissingPixmap;
}

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

void DvDirVersionControlProjectNode::refreshChildren()
{
	DvDirModelFileFolderNode::refreshChildren();
	int i;
	TProjectManager *pm = TProjectManager::instance();
	TProject *project = new TProject();
	project->load(getProjectPath());
	for (i = 0; i < getChildCount(); i++) {
		DvDirModelFileFolderNode *node =
			dynamic_cast<DvDirModelFileFolderNode *>(getChild(i));
		if (node) {
			int k = project->getFolderIndexFromPath(node->getPath());
			node->setIsProjectFolder(k >= 0);
		}
	}
	delete project;
}

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

void DvDirVersionControlProjectNode::getChildrenNames(std::vector<wstring> &names) const
{
	DvDirVersionControlNode::getChildrenNames(names);
	TProjectManager *pm = TProjectManager::instance();
	TProject *project = new TProject();
	project->load(getProjectPath());
	int i;
	for (i = 0; i < project->getFolderCount(); i++) {
		string folderName = project->getFolderName(i);
		TFilePath folderPath = project->getFolder(i);
		//if(folderPath.isAbsolute() || folderPath.getParentDir() != TFilePath())
		if (folderPath.isAbsolute() && project->isConstantFolder(i)) {
			names.push_back(L"+" + toWideString(folderName));
		}
	}
	delete project;
}

//=============================================================================
//
// DvDirModelProjectNode [Project Folder]
//
//-----------------------------------------------------------------------------

DvDirModelProjectNode::DvDirModelProjectNode(DvDirModelNode *parent, const TFilePath &path)
	: DvDirModelFileFolderNode(parent, path)
{
	m_nodeType = "Project";
}

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

TFilePath DvDirModelProjectNode::getProjectPath() const
{
	return TProjectManager::instance()->projectFolderToProjectPath(getPath());
}

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

bool DvDirModelProjectNode::isCurrent() const
{
	TProjectManager *pm = TProjectManager::instance();
	return pm->getCurrentProjectPath().getParentDir() == getPath();
}

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

void DvDirModelProjectNode::makeCurrent()
{
	TProjectManager *pm = TProjectManager::instance();
	TFilePath projectPath = getProjectPath();
	if (!IoCmd::saveSceneIfNeeded(QObject::tr("Change project")))
		return;

#ifdef LINETEST
	TnzCamera *camera = TnzCamera::instance();
	if (camera->isCameraConnected())
		camera->cameraDisconnect();
#endif

	pm->setCurrentProjectPath(projectPath);
	IoCmd::newScene();
}

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

QPixmap DvDirModelProjectNode::getPixmap(bool isOpen) const
{
	static QPixmap openProjectPixmap(":Resources/browser_project_open.png");
	static QPixmap closeProjectPixmap(":Resources/browser_project_close.png");
	return isOpen ? openProjectPixmap : closeProjectPixmap;
}

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

void DvDirModelProjectNode::refreshChildren()
{
	DvDirModelFileFolderNode::refreshChildren();
	int i;
	TProjectManager *pm = TProjectManager::instance();
	TProject *project = new TProject();
	project->load(getProjectPath());
	for (i = 0; i < getChildCount(); i++) {
		DvDirModelFileFolderNode *node =
			dynamic_cast<DvDirModelFileFolderNode *>(getChild(i));
		if (node) {
			int k = project->getFolderIndexFromPath(node->getPath());
			node->setIsProjectFolder(k >= 0);
		}
	}
	delete project;
}

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

void DvDirModelProjectNode::getChildrenNames(std::vector<wstring> &names) const
{
	DvDirModelFileFolderNode::getChildrenNames(names);
	TProjectManager *pm = TProjectManager::instance();
	TProject *project = new TProject();
	project->load(getProjectPath());
	int i;
	for (i = 0; i < project->getFolderCount(); i++) {
		string folderName = project->getFolderName(i);
		TFilePath folderPath = project->getFolder(i);
		//if(folderPath.isAbsolute() || folderPath.getParentDir() != TFilePath())
		if (folderPath.isAbsolute() && project->isConstantFolder(i)) {
			names.push_back(L"+" + toWideString(folderName));
		}
	}
	delete project;
}

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

DvDirModelNode *DvDirModelProjectNode::makeChild(wstring name)
{
	if (name != L"" && name[0] == L'+') {
		TProjectManager *pm = TProjectManager::instance();
		TProject *project = new TProject();
		project->load(getProjectPath());
		string folderName = toString(name.substr(1));
		TFilePath folderPath = project->getFolder(folderName);
		DvDirModelNode *node = new DvDirModelFileFolderNode(this, name, folderPath);
		delete project;
		return node;
	} else
		return DvDirModelFileFolderNode::makeChild(name);
}

//=============================================================================
//
// DvDirModelDayNode [Day folder]
//
//-----------------------------------------------------------------------------

DvDirModelDayNode::DvDirModelDayNode(DvDirModelNode *parent, wstring dayDateString)
	: DvDirModelNode(parent, dayDateString), m_dayDateString(toString(dayDateString))
{
	m_nodeType = "Day";
}

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

void DvDirModelDayNode::visualizeContent(FileBrowser *browser)
{
	browser->setHistoryDay(m_dayDateString);
}

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

QPixmap DvDirModelDayNode::getPixmap(bool isOpen) const
{
	static QPixmap openFolderPixmap(":Resources/browser_folder_open.png");
	static QPixmap closeFolderPixmap(":Resources/browser_folder_close.png");
	return isOpen ? openFolderPixmap : closeFolderPixmap;
}

//=============================================================================
//
// DvDirModelHistoryNode [History]
//
//-----------------------------------------------------------------------------

DvDirModelHistoryNode::DvDirModelHistoryNode(DvDirModelNode *parent)
	: DvDirModelNode(parent, L"History")
{
	m_nodeType = "History";
}

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

void DvDirModelHistoryNode::refreshChildren()
{
	m_children.clear();
	m_childrenValid = true;
	History *h = History::instance();
	for (int i = 0; i < h->getDayCount(); i++) {
		const History::Day *day = h->getDay(i);
		DvDirModelNode *child = new DvDirModelDayNode(this, toWideString(day->getDate()));
		addChild(child);
	}
}

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

QPixmap DvDirModelHistoryNode::getPixmap(bool isOpen) const
{
	static QPixmap pixmap(":Resources/history.png");
	return pixmap;
}

//=============================================================================
//
// DvDirModelMyComputerNode [My Computer]
//
//-----------------------------------------------------------------------------

DvDirModelMyComputerNode::DvDirModelMyComputerNode(DvDirModelNode *parent)
	: DvDirModelNode(parent, L"My Computer")
{
	// setIcon(QFileIconProvider().icon(QFileIconProvider::Computer));
	m_nodeType = "MyComputer";
}

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

void DvDirModelMyComputerNode::refreshChildren()
{
	m_childrenValid = true;
	if (!m_children.empty())
		clearPointerContainer(m_children);

	TFilePathSet fps = TSystem::getDisks();

#ifdef MACOSX
	fps.push_back(TFilePath("/Volumes/"));
#endif

	TFilePathSet::iterator it;
	for (it = fps.begin(); it != fps.end(); ++it) {
		DvDirModelNode *child = new DvDirModelFileFolderNode(this, it->getWideString(), *it);
		addChild(child);
	}
}

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

QPixmap DvDirModelMyComputerNode::getPixmap(bool isOpen) const
{
	static QPixmap pixmap(":Resources/my_computer.png");
	return pixmap;
}

//=============================================================================
//
// DvDirModelNetworkNode [Network]
//
//-----------------------------------------------------------------------------

DvDirModelNetworkNode::DvDirModelNetworkNode(DvDirModelNode *parent)
	: DvDirModelNode(parent, L"Network")
{
	m_nodeType = "Network";
}

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

void DvDirModelNetworkNode::refreshChildren()
{
	m_childrenValid = true;

	if (!m_children.empty())
		clearPointerContainer(m_children);

#ifdef WIN32

	// Enumerate network nodes
	HANDLE enumHandle;
	DWORD err = WNetOpenEnum(RESOURCE_CONTEXT, RESOURCETYPE_DISK, 0, NULL, &enumHandle);
	assert(err == NO_ERROR);

	DWORD count = -1, bufferSize = 16 << 10; // Use 16 kB as recommended on Windows references
	LPNETRESOURCE buffer = (LPNETRESOURCE)malloc(bufferSize);

	do {
		// Get the next bunch of network devices
		memset(buffer, 0, bufferSize);
		err = WNetEnumResource(enumHandle, &count, buffer, &bufferSize);

		if (err == NO_ERROR) {
			for (int i = 0; i < count; ++i) {
				// Only list disk-type devices - in any case, the remote (UNC) name should exist
				if (buffer[i].dwType == RESOURCETYPE_DISK && buffer[i].lpRemoteName) {
					std::wstring wstr(buffer[i].lpRemoteName);

					// Build a NOT PEEKING folder node. This is important since network access is SLOW.
					DvDirModelFileFolderNode *child = new DvDirModelFileFolderNode(this, wstr, TFilePath(wstr));
					child->setPeeking(false);

					addChild(child);
				}
			}
		} else if (err != ERROR_NO_MORE_ITEMS)
			break; // Excluding NO_ERROR and ERROR_NO_MORE_ITEMS, quit

	} while (err != ERROR_NO_MORE_ITEMS);

	err = WNetCloseEnum(enumHandle);
	assert(err == NO_ERROR);

	free(buffer);

#endif
}

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

QPixmap DvDirModelNetworkNode::getPixmap(bool isOpen) const
{
	static QPixmap pixmap(":Resources/network.png");
	return pixmap;
}

//=============================================================================
//
// DvDirModelRootNode [Root]
//
//-----------------------------------------------------------------------------

DvDirModelRootNode::DvDirModelRootNode()
	: DvDirModelNode(0, L"Root"), m_myComputerNode(0), m_networkNode(0), m_sandboxProjectNode(0)
{
	m_nodeType = "Root";
}

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

void DvDirModelRootNode::add(wstring name, const TFilePath &path)
{
	DvDirModelNode *child = new DvDirModelFileFolderNode(this, name, path);
	child->setRow((int)m_children.size());
	m_children.push_back(child);
}

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

void DvDirModelRootNode::refreshChildren()
{
	m_childrenValid = true;
	if (m_children.empty()) {
		addChild(m_myComputerNode = new DvDirModelMyComputerNode(this));

#ifdef WIN32
		addChild(m_networkNode = new DvDirModelNetworkNode(this));
#endif

		DvDirModelSpecialFileFolderNode *child;
		child = new DvDirModelSpecialFileFolderNode(this, L"My Documents", getMyDocumentsPath());
		child->setPixmap(QPixmap(":Resources/my_documents.png"));
		addChild(child);

		child = new DvDirModelSpecialFileFolderNode(this, L"Library", ToonzFolder::getLibraryFolder());
		child->setPixmap(QPixmap(":Resources/library.png"));
		addChild(child);

		addChild(new DvDirModelHistoryNode(this));

		TProjectManager *pm = TProjectManager::instance();
		std::vector<TFilePath> projectRoots;
		pm->getProjectRoots(projectRoots);

		int i;
		for (i = 0; i < (int)projectRoots.size(); i++) {
			TFilePath projectRoot = projectRoots[i];
			wstring roothDir = projectRoot.getWideString();
			DvDirModelSpecialFileFolderNode *projectRootNode =
				new DvDirModelSpecialFileFolderNode(this, L"Project root (" + roothDir + L")", projectRoot);
			projectRootNode->setPixmap(QPixmap(":Resources/projects.png"));
			m_projectRootNodes.push_back(projectRootNode);
			addChild(projectRootNode);
		}

		TFilePath sandboxProjectPath = pm->getSandboxProjectFolder();
		m_sandboxProjectNode =
			new DvDirModelProjectNode(this, sandboxProjectPath);
		addChild(m_sandboxProjectNode);

		// SVN Repositories
		QList<SVNRepository> repositories = VersionControl::instance()->getRepositories();
		int count = repositories.size();
		for (int i = 0; i < count; i++) {
			SVNRepository repo = repositories.at(i);
			DvDirVersionControlRootNode *node =
				new DvDirVersionControlRootNode(this, repo.m_name.toStdWString(), TFilePath(repo.m_localPath.toStdWString()));
			node->setRepositoryPath(repo.m_repoPath.toStdWString());
			node->setLocalPath(repo.m_localPath.toStdWString());
			node->setUserName(repo.m_username.toStdWString());
			node->setPassword(repo.m_password.toStdWString());
			m_versionControlNodes.push_back(node);
			addChild(node);
		}
	}
}

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

DvDirModelNode *DvDirModelRootNode::getNodeByPath(const TFilePath &path)
{
	// Check first for version control nodes
	DvDirModelNode *node = 0;
	int i;

	//search in #1 the project folders, #2 sandbox, #3 other folders in file system

	//path could be a project, under some project root
	for (i = 0; i < (int)m_projectRootNodes.size(); i++) {
		node = m_projectRootNodes[i]->getNodeByPath(path);
		if (node)
			return node;
		//search in the project folders
		for (int j = 0; j < m_projectRootNodes[i]->getChildCount(); j++) {
			DvDirModelProjectNode *projectNode = dynamic_cast<DvDirModelProjectNode *>(m_projectRootNodes[i]->getChild(j));
			if (projectNode) {
				// for the normal folder in the project folder
				node = projectNode->getNodeByPath(path);
				if (node)
					return node;

				if (projectNode->isCurrent()) {
					// for the aliases in the project folder ("+drawings" etc.)
					for (int k = 0; k < projectNode->getChildCount(); k++) {
						node = projectNode->getChild(k)->getNodeByPath(path);
						if (node)
							return node;
					}
				}
			} else //for the normal folder in the project root
			{
				node = m_projectRootNodes[i]->getChild(j)->getNodeByPath(path);
				if (node)
					return node;
			}
		}
	}

	// it could be on a repository, under version control
	for (i = 0; i < (int)m_versionControlNodes.size(); i++) {
		node = m_versionControlNodes[i]->getNodeByPath(path);
		if (node)
			return node;
	}

	// or it could be the sandbox project or in the sandbox project
	if (m_sandboxProjectNode && m_sandboxProjectNode->getPath() == path)
		return m_sandboxProjectNode;
	if (m_sandboxProjectNode) {
		for (i = 0; i < m_sandboxProjectNode->getChildCount(); i++) {
			DvDirModelNode *node = m_sandboxProjectNode->getChild(i)->getNodeByPath(path);
			if (node)
				return node;
		}
	}

	// it could be a regular folder, somewhere in the file system
	if (m_myComputerNode) {
		for (i = 0; i < m_myComputerNode->getChildCount(); i++) {
			DvDirModelNode *node = m_myComputerNode->getChild(i)->getNodeByPath(path);
			if (node)
				return node;
		}
	}

	// it could be a network folder
	if (m_networkNode) {
		for (i = 0; i < m_networkNode->getChildCount(); i++) {
			DvDirModelNode *node = m_networkNode->getChild(i)->getNodeByPath(path);
			if (node)
				return node;
		}
	}

	return 0;
}

//=============================================================================
//
// DvDirModel
//
//-----------------------------------------------------------------------------

DvDirModel::DvDirModel()
{
	m_root = new DvDirModelRootNode();
	m_root->refreshChildren();
}

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

DvDirModel::~DvDirModel()
{
	delete m_root;
}

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

void DvDirModel::onFolderChanged(const TFilePath &path)
{
	refreshFolder(path);
}

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

void DvDirModel::refresh(const QModelIndex &index)
{
	if (!index.isValid())
		return;
	DvDirModelNode *node = getNode(index);
	if (!node)
		return;
	emit layoutAboutToBeChanged();
	emit beginRemoveRows(index, 0, node->getChildCount());
	node->refreshChildren();
	emit endRemoveRows();
	emit layoutChanged();
}

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

void DvDirModel::refreshFolder(const TFilePath &folderPath, const QModelIndex &i)
{
	DvDirModelNode *node = getNode(i);
	if (!node)
		return;
	if (node->isFolder(folderPath))
		refresh(i);
	else if (node->areChildrenValid()) {
		int count = rowCount(i);
		int r;
		for (r = 0; r < count; r++)
			refreshFolder(folderPath, index(r, 0, i));
	}
}

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

void DvDirModel::refreshFolderChild(const QModelIndex &i)
{
	DvDirModelNode *node = getNode(i);
	if (!node || !node->areChildrenValid())
		return;

	if (node->isFolder() || dynamic_cast<DvDirModelMyComputerNode *>(node))
		refresh(i);
	int count = rowCount(i);
	int r;
	for (r = 0; r < count; r++)
		refreshFolderChild(index(r, 0, i));
}

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

DvDirModelNode *DvDirModel::getNode(const QModelIndex &index) const
{
	if (index.isValid())
		return static_cast<DvDirModelNode *>(index.internalPointer());
	else
		return m_root;
}

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

QModelIndex DvDirModel::index(int row, int column, const QModelIndex &parent) const
{
	if (column != 0)
		return QModelIndex();
	DvDirModelNode *parentNode = m_root;
	if (parent.isValid())
		parentNode = getNode(parent);
	if (row < 0 || row >= parentNode->getChildCount())
		return QModelIndex();
	DvDirModelNode *node = parentNode->getChild(row);
	return createIndex(row, column, node);
}

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

QModelIndex DvDirModel::parent(const QModelIndex &index) const
{
	if (!index.isValid())
		return QModelIndex();
	DvDirModelNode *node = getNode(index);
	DvDirModelNode *parentNode = node->getParent();
	if (!parentNode || parentNode == m_root)
		return QModelIndex();
	else
		return createIndex(parentNode->getRow(), 0, parentNode);
}

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

QModelIndex DvDirModel::childByName(const QModelIndex &parent, const wstring &name) const
{
	if (!parent.isValid())
		return QModelIndex();
	DvDirModelNode *parentNode = getNode(parent);
	if (!parentNode)
		return QModelIndex();
	int row = parentNode->rowByName(name);
	if (row < 0 || row >= parentNode->getChildCount())
		return QModelIndex();
	DvDirModelNode *childNode = parentNode->getChild(row);
	return createIndex(row, 0, childNode);
}

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

int DvDirModel::rowCount(const QModelIndex &parent) const
{
	DvDirModelNode *node = getNode(parent);
	return node->getChildCount();
}

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

bool DvDirModel::hasChildren(const QModelIndex &parent) const
{
	DvDirModelNode *node = getNode(parent);
	return node->hasChildren();
}

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

QVariant DvDirModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
		return QVariant();
	DvDirModelNode *node = getNode(index);
	if (role == Qt::DisplayRole || role == Qt::EditRole)
		return QString::fromStdWString(node->getName());
	else if (role == Qt::DecorationRole) {
		return QVariant();
	} else if (role == Qt::ForegroundRole) {
		if (!node || !node->isRenameEnabled())
			return QBrush(Qt::blue);
		else
			return QVariant();
	} else
		return QVariant();
}

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

Qt::ItemFlags DvDirModel::flags(const QModelIndex &index) const
{
	Qt::ItemFlags flags = QAbstractItemModel::flags(index);
	if (index.isValid()) {
		DvDirModelNode *node = getNode(index);
		if (node && node->isRenameEnabled())
			flags |= Qt::ItemIsEditable;
	}
	return flags;
}

//-----------------------------------------------------------------------------
/*! used only for name / rename of items
*/
bool DvDirModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
	if (!index.isValid())
		return false;
	DvDirModelNode *node = getNode(index);
	if (!node || !node->isRenameEnabled())
		return false;
	QString newName = value.toString();
	if (newName == "")
		return false;
	if (!node->setName(newName.toStdWString()))
		return false;
	emit dataChanged(index, index);
	return true;
}

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

bool DvDirModel::removeRows(int row, int count, const QModelIndex &parentIndex)
{
	if (!parentIndex.isValid())
		return false;
	DvDirModelNode *node = getNode(parentIndex);
	if (!node)
		return false;
	emit beginRemoveRows(parentIndex, row, row + count - 1);
	node->removeChildren(row, count);
	emit endRemoveRows();
	return true;
}

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

QModelIndex DvDirModel::getIndexByPath(const TFilePath &path) const
{
	return getIndexByNode(m_root->getNodeByPath(path));
}

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

QModelIndex DvDirModel::getIndexByNode(DvDirModelNode *node) const
{
	if (!node)
		return QModelIndex();
	return createIndex(node->getRow(), 0, node);
}

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

QModelIndex DvDirModel::getCurrentProjectIndex() const
{
	return getIndexByPath(TProjectManager::instance()->getCurrentProjectPath().getParentDir());
}

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

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