|
|
@@ -11,6 +11,8 @@
|
|
|
#include "CmImporter.h"
|
|
|
#include "CmImportOptions.h"
|
|
|
#include "CmFileSerializer.h"
|
|
|
+#include "CmFolderMonitor.h"
|
|
|
+#include "CmDebug.h"
|
|
|
|
|
|
using namespace CamelotFramework;
|
|
|
using namespace BansheeEngine;
|
|
|
@@ -19,46 +21,37 @@ namespace BansheeEditor
|
|
|
{
|
|
|
const WString ProjectLibrary::INTERNAL_RESOURCES_DIR = L"Internal/Resources";
|
|
|
|
|
|
- enum class LibraryEntryType
|
|
|
- {
|
|
|
- File,
|
|
|
- Directory
|
|
|
- };
|
|
|
-
|
|
|
- struct ProjectLibrary::LibraryEntry
|
|
|
- {
|
|
|
- LibraryEntryType type;
|
|
|
- WPath path;
|
|
|
-
|
|
|
- DirectoryEntry* parent;
|
|
|
- };
|
|
|
-
|
|
|
- struct ProjectLibrary::ResourceEntry : public ProjectLibrary::LibraryEntry
|
|
|
- {
|
|
|
- ResourceMetaPtr meta;
|
|
|
- std::time_t lastUpdateTime;
|
|
|
- };
|
|
|
-
|
|
|
- struct ProjectLibrary::DirectoryEntry : public ProjectLibrary::LibraryEntry
|
|
|
- {
|
|
|
- Vector<LibraryEntry*>::type mChildren;
|
|
|
- };
|
|
|
-
|
|
|
ProjectLibrary::ProjectLibrary()
|
|
|
:mRootEntry(nullptr)
|
|
|
{
|
|
|
+ mMonitor = cm_new<FolderMonitor>();
|
|
|
+
|
|
|
+ FolderChange folderChanges = (FolderChange)((UINT32)FolderChange::FileName | (UINT32)FolderChange::DirName |
|
|
|
+ (UINT32)FolderChange::Creation | (UINT32)FolderChange::LastWrite);
|
|
|
+ mMonitor->startMonitor(EditorApplication::instance().getResourcesFolderPath(), true, folderChanges);
|
|
|
|
|
|
+ mMonitor->onAdded.connect(boost::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
|
|
|
+ mMonitor->onRemoved.connect(boost::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
|
|
|
+ mMonitor->onModified.connect(boost::bind(&ProjectLibrary::onMonitorFileModified, this, _1));
|
|
|
}
|
|
|
|
|
|
ProjectLibrary::~ProjectLibrary()
|
|
|
{
|
|
|
+ mMonitor->stopMonitorAll();
|
|
|
+ cm_delete(mMonitor);
|
|
|
+
|
|
|
if(mRootEntry != nullptr)
|
|
|
- deleteDirectory(mRootEntry);
|
|
|
+ deleteDirectoryInternal(mRootEntry);
|
|
|
}
|
|
|
|
|
|
- void ProjectLibrary::checkForModifications(const CM::WString& folder)
|
|
|
+ void ProjectLibrary::update()
|
|
|
{
|
|
|
- if(!PathUtil::includes(folder, EditorApplication::instance().getResourcesFolderPath()))
|
|
|
+ mMonitor->update();
|
|
|
+ }
|
|
|
+
|
|
|
+ void ProjectLibrary::checkForModifications(const CM::WString& fullPath)
|
|
|
+ {
|
|
|
+ if(!PathUtil::includes(fullPath, EditorApplication::instance().getResourcesFolderPath()))
|
|
|
return; // Folder not part of our resources path, so no modifications
|
|
|
|
|
|
if(mRootEntry == nullptr)
|
|
|
@@ -67,120 +60,165 @@ namespace BansheeEditor
|
|
|
mRootEntry->path = toPath(EditorApplication::instance().getResourcesFolderPath());
|
|
|
}
|
|
|
|
|
|
- DirectoryEntry* directoryEntry = findDirectoryEntry(folder);
|
|
|
- if(directoryEntry == nullptr)
|
|
|
+ WPath pathToSearch = toPath(fullPath);
|
|
|
+ LibraryEntry* entry = findEntry(pathToSearch);
|
|
|
+ if(entry == nullptr) // File could be new, try to find parent directory entry
|
|
|
{
|
|
|
- CM_EXCEPT(InternalErrorException, "Attempting to check for modifications on a folder that is not yet part " \
|
|
|
- "of the project library. Maybe check for modifications on the parent folder first? Folder: \"" + toString(folder) + "\"");
|
|
|
- }
|
|
|
+ WPath parentDirPath = PathUtil::parentPath(pathToSearch);
|
|
|
+ entry = findEntry(parentDirPath);
|
|
|
+
|
|
|
+ // Cannot find parent directory. Create the needed hierarchy.
|
|
|
+ DirectoryEntry* entryParent = nullptr;
|
|
|
+ DirectoryEntry* newHierarchyParent = nullptr;
|
|
|
+ if(entry == nullptr)
|
|
|
+ createInternalParentHierarchy(pathToSearch, &newHierarchyParent, &entryParent);
|
|
|
+ else
|
|
|
+ entryParent = static_cast<DirectoryEntry*>(entry);
|
|
|
|
|
|
- Stack<DirectoryEntry*>::type todo;
|
|
|
- todo.push(directoryEntry);
|
|
|
+ if(FileSystem::isFile(pathToSearch))
|
|
|
+ {
|
|
|
+ addResourceInternal(entryParent, pathToSearch);
|
|
|
+ }
|
|
|
+ else if(FileSystem::isDirectory(pathToSearch))
|
|
|
+ {
|
|
|
+ addDirectoryInternal(entryParent, pathToSearch);
|
|
|
|
|
|
- Vector<WPath>::type childFiles;
|
|
|
- Vector<WPath>::type childDirectories;
|
|
|
- Vector<bool>::type existingEntries;
|
|
|
- Vector<LibraryEntry*>::type toDelete;
|
|
|
+ if(newHierarchyParent == nullptr)
|
|
|
+ checkForModifications(toWString(pathToSearch));
|
|
|
+ }
|
|
|
|
|
|
- while(!todo.empty())
|
|
|
+ if(newHierarchyParent != nullptr)
|
|
|
+ checkForModifications(toWString(newHierarchyParent->path));
|
|
|
+ }
|
|
|
+ else if(entry->type == LibraryEntryType::File)
|
|
|
{
|
|
|
- DirectoryEntry* currentDir = todo.top();
|
|
|
- todo.pop();
|
|
|
+ if(FileSystem::isFile(entry->path))
|
|
|
+ {
|
|
|
+ reimportResourceInternal(static_cast<ResourceEntry*>(entry));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ deleteResourceInternal(static_cast<ResourceEntry*>(entry));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(entry->type == LibraryEntryType::Directory) // Check folder and all subfolders for modifications
|
|
|
+ {
|
|
|
+ Stack<DirectoryEntry*>::type todo;
|
|
|
+ todo.push(static_cast<DirectoryEntry*>(entry));
|
|
|
+
|
|
|
+ Vector<WPath>::type childFiles;
|
|
|
+ Vector<WPath>::type childDirectories;
|
|
|
+ Vector<bool>::type existingEntries;
|
|
|
+ Vector<LibraryEntry*>::type toDelete;
|
|
|
+
|
|
|
+ while(!todo.empty())
|
|
|
+ {
|
|
|
+ DirectoryEntry* currentDir = todo.top();
|
|
|
+ todo.pop();
|
|
|
|
|
|
- existingEntries.clear();
|
|
|
- existingEntries.resize(currentDir->mChildren.size());
|
|
|
- for(UINT32 i = 0; i < (UINT32)currentDir->mChildren.size(); i++)
|
|
|
- existingEntries[i] = false;
|
|
|
+ existingEntries.clear();
|
|
|
+ existingEntries.resize(currentDir->mChildren.size());
|
|
|
+ for(UINT32 i = 0; i < (UINT32)currentDir->mChildren.size(); i++)
|
|
|
+ existingEntries[i] = false;
|
|
|
|
|
|
- childFiles.clear();
|
|
|
- childDirectories.clear();
|
|
|
+ childFiles.clear();
|
|
|
+ childDirectories.clear();
|
|
|
|
|
|
- FileSystem::getChildren(currentDir->path, childFiles, childDirectories);
|
|
|
+ FileSystem::getChildren(currentDir->path, childFiles, childDirectories);
|
|
|
|
|
|
- for(auto& filePath : childFiles)
|
|
|
- {
|
|
|
- ResourceEntry* existingEntry = nullptr;
|
|
|
- UINT32 idx = 0;
|
|
|
- for(auto& child : currentDir->mChildren)
|
|
|
+ for(auto& filePath : childFiles)
|
|
|
{
|
|
|
- if(child->type == LibraryEntryType::File && child->path == filePath)
|
|
|
+ if(isMeta(filePath))
|
|
|
{
|
|
|
- existingEntries[idx] = true;
|
|
|
- existingEntry = static_cast<ResourceEntry*>(child);
|
|
|
- break;
|
|
|
- }
|
|
|
+ WPath sourceFilePath = filePath;
|
|
|
+ PathUtil::replaceExtension(sourceFilePath, L"");
|
|
|
|
|
|
- idx++;
|
|
|
- }
|
|
|
+ if(FileSystem::isFile(sourceFilePath))
|
|
|
+ {
|
|
|
+ LOGWRN("Found a .meta file without a corresponding resource. Deleting.");
|
|
|
|
|
|
- if(existingEntry != nullptr)
|
|
|
- {
|
|
|
- reimportResource(existingEntry);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- addResource(currentDir, filePath);
|
|
|
+ FileSystem::remove(filePath);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ResourceEntry* existingEntry = nullptr;
|
|
|
+ UINT32 idx = 0;
|
|
|
+ for(auto& child : currentDir->mChildren)
|
|
|
+ {
|
|
|
+ if(child->type == LibraryEntryType::File && child->path == filePath)
|
|
|
+ {
|
|
|
+ existingEntries[idx] = true;
|
|
|
+ existingEntry = static_cast<ResourceEntry*>(child);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ idx++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(existingEntry != nullptr)
|
|
|
+ {
|
|
|
+ reimportResourceInternal(existingEntry);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ addResourceInternal(currentDir, filePath);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- for(auto& dirPath : childDirectories)
|
|
|
- {
|
|
|
- DirectoryEntry* existingEntry = nullptr;
|
|
|
- UINT32 idx = 0;
|
|
|
- for(auto& child : currentDir->mChildren)
|
|
|
+ for(auto& dirPath : childDirectories)
|
|
|
{
|
|
|
- if(child->type == LibraryEntryType::Directory && child->path == dirPath)
|
|
|
+ DirectoryEntry* existingEntry = nullptr;
|
|
|
+ UINT32 idx = 0;
|
|
|
+ for(auto& child : currentDir->mChildren)
|
|
|
{
|
|
|
- existingEntries[idx] = true;
|
|
|
- existingEntry = static_cast<DirectoryEntry*>(child);
|
|
|
- break;
|
|
|
+ if(child->type == LibraryEntryType::Directory && child->path == dirPath)
|
|
|
+ {
|
|
|
+ existingEntries[idx] = true;
|
|
|
+ existingEntry = static_cast<DirectoryEntry*>(child);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ idx++;
|
|
|
}
|
|
|
|
|
|
- idx++;
|
|
|
+ if(existingEntry == nullptr)
|
|
|
+ addDirectoryInternal(currentDir, dirPath);
|
|
|
}
|
|
|
|
|
|
- if(existingEntry == nullptr)
|
|
|
{
|
|
|
- DirectoryEntry* newEntry = cm_new<DirectoryEntry>();
|
|
|
- newEntry->path = dirPath;
|
|
|
- newEntry->type = LibraryEntryType::Directory;
|
|
|
- newEntry->parent = currentDir;
|
|
|
-
|
|
|
- currentDir->mChildren.push_back(newEntry);
|
|
|
- }
|
|
|
- }
|
|
|
+ UINT32 idx = 0;
|
|
|
+ toDelete.clear();
|
|
|
+ for(auto& child : currentDir->mChildren)
|
|
|
+ {
|
|
|
+ if(existingEntries[idx])
|
|
|
+ continue;
|
|
|
|
|
|
- {
|
|
|
- UINT32 idx = 0;
|
|
|
- toDelete.clear();
|
|
|
- for(auto& child : currentDir->mChildren)
|
|
|
- {
|
|
|
- if(existingEntries[idx])
|
|
|
- continue;
|
|
|
+ toDelete.push_back(child);
|
|
|
|
|
|
- toDelete.push_back(child);
|
|
|
+ idx++;
|
|
|
+ }
|
|
|
|
|
|
- idx++;
|
|
|
+ for(auto& child : toDelete)
|
|
|
+ {
|
|
|
+ if(child->type == LibraryEntryType::Directory)
|
|
|
+ deleteDirectoryInternal(static_cast<DirectoryEntry*>(child));
|
|
|
+ else if(child->type == LibraryEntryType::File)
|
|
|
+ deleteResourceInternal(static_cast<ResourceEntry*>(child));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- for(auto& child : toDelete)
|
|
|
+ for(auto& child : currentDir->mChildren)
|
|
|
{
|
|
|
if(child->type == LibraryEntryType::Directory)
|
|
|
- deleteDirectory(static_cast<DirectoryEntry*>(child));
|
|
|
- else if(child->type == LibraryEntryType::File)
|
|
|
- deleteResource(static_cast<ResourceEntry*>(child));
|
|
|
+ todo.push(static_cast<DirectoryEntry*>(child));
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- for(auto& child : currentDir->mChildren)
|
|
|
- {
|
|
|
- if(child->type == LibraryEntryType::Directory)
|
|
|
- todo.push(static_cast<DirectoryEntry*>(child));
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void ProjectLibrary::addResource(DirectoryEntry* parent, const CM::WPath& filePath)
|
|
|
+ ProjectLibrary::ResourceEntry* ProjectLibrary::addResourceInternal(DirectoryEntry* parent, const CM::WPath& filePath)
|
|
|
{
|
|
|
ResourceEntry* newResource = cm_new<ResourceEntry>();
|
|
|
newResource->type = LibraryEntryType::File;
|
|
|
@@ -189,10 +227,83 @@ namespace BansheeEditor
|
|
|
|
|
|
parent->mChildren.push_back(newResource);
|
|
|
|
|
|
- reimportResource(newResource);
|
|
|
+ reimportResourceInternal(newResource);
|
|
|
+
|
|
|
+ if(!onEntryAdded.empty())
|
|
|
+ onEntryAdded(filePath);
|
|
|
+
|
|
|
+ return newResource;
|
|
|
}
|
|
|
|
|
|
- void ProjectLibrary::reimportResource(ResourceEntry* resource)
|
|
|
+ ProjectLibrary::DirectoryEntry* ProjectLibrary::addDirectoryInternal(DirectoryEntry* parent, const CM::WPath& dirPath)
|
|
|
+ {
|
|
|
+ DirectoryEntry* newEntry = cm_new<DirectoryEntry>();
|
|
|
+ newEntry->type = LibraryEntryType::Directory;
|
|
|
+ newEntry->path = dirPath;
|
|
|
+ newEntry->parent = parent;
|
|
|
+
|
|
|
+ parent->mChildren.push_back(newEntry);
|
|
|
+
|
|
|
+ if(!onEntryAdded.empty())
|
|
|
+ onEntryAdded(dirPath);
|
|
|
+
|
|
|
+ return newEntry;
|
|
|
+ }
|
|
|
+
|
|
|
+ void ProjectLibrary::deleteResourceInternal(ResourceEntry* resource)
|
|
|
+ {
|
|
|
+ ResourceManifestPtr manifest = gResources().getResourceManifest();
|
|
|
+
|
|
|
+ if(resource->meta != nullptr)
|
|
|
+ {
|
|
|
+ if(manifest->uuidExists(resource->meta->getUUID()))
|
|
|
+ {
|
|
|
+ const WPath& path = manifest->uuidToFilePath(resource->meta->getUUID());
|
|
|
+ if(FileSystem::isFile(path))
|
|
|
+ FileSystem::remove(path);
|
|
|
+
|
|
|
+ manifest->unregisterResource(resource->meta->getUUID());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ DirectoryEntry* parent = resource->parent;
|
|
|
+ auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
|
|
|
+ [&] (const LibraryEntry* entry) { return entry == resource; });
|
|
|
+
|
|
|
+ parent->mChildren.erase(findIter);
|
|
|
+
|
|
|
+ if(!onEntryRemoved.empty())
|
|
|
+ onEntryRemoved(resource->path);
|
|
|
+
|
|
|
+ cm_delete(resource);
|
|
|
+ }
|
|
|
+
|
|
|
+ void ProjectLibrary::deleteDirectoryInternal(DirectoryEntry* directory)
|
|
|
+ {
|
|
|
+ if(directory == mRootEntry)
|
|
|
+ mRootEntry = nullptr;
|
|
|
+
|
|
|
+ for(auto& child : directory->mChildren)
|
|
|
+ {
|
|
|
+ if(child->type == LibraryEntryType::Directory)
|
|
|
+ deleteDirectoryInternal(static_cast<DirectoryEntry*>(child));
|
|
|
+ else
|
|
|
+ deleteResourceInternal(static_cast<ResourceEntry*>(child));
|
|
|
+ }
|
|
|
+
|
|
|
+ DirectoryEntry* parent = directory->parent;
|
|
|
+ auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
|
|
|
+ [&] (const LibraryEntry* entry) { return entry == directory; });
|
|
|
+
|
|
|
+ parent->mChildren.erase(findIter);
|
|
|
+
|
|
|
+ if(!onEntryRemoved.empty())
|
|
|
+ onEntryRemoved(directory->path);
|
|
|
+
|
|
|
+ cm_delete(directory);
|
|
|
+ }
|
|
|
+
|
|
|
+ void ProjectLibrary::reimportResourceInternal(ResourceEntry* resource)
|
|
|
{
|
|
|
WString ext = toWString(PathUtil::getExtension(resource->path));
|
|
|
WPath metaPath = resource->path;
|
|
|
@@ -220,7 +331,7 @@ namespace BansheeEditor
|
|
|
if(!isUpToDate(resource))
|
|
|
{
|
|
|
ImportOptionsPtr importOptions = nullptr;
|
|
|
-
|
|
|
+
|
|
|
if(resource->meta != nullptr)
|
|
|
importOptions = resource->meta->getImportOptions();
|
|
|
else
|
|
|
@@ -232,7 +343,7 @@ namespace BansheeEditor
|
|
|
{
|
|
|
resource->meta = ResourceMeta::create(importedResource.getUUID(), importOptions);
|
|
|
FileSerializer fs;
|
|
|
- fs.encode(resource->meta.get(), toWString(metaPath)); // TODO - Make it accept WPath
|
|
|
+ fs.encode(resource->meta.get(), toWString(metaPath));
|
|
|
}
|
|
|
|
|
|
WPath internalResourcesPath = toPath(EditorApplication::instance().getActiveProjectPath()) / toPath(INTERNAL_RESOURCES_DIR);
|
|
|
@@ -242,104 +353,224 @@ namespace BansheeEditor
|
|
|
internalResourcesPath /= toPath(toWString(importedResource.getUUID()) + L".asset");
|
|
|
|
|
|
gResources().save(importedResource, toWString(internalResourcesPath), true);
|
|
|
+
|
|
|
+ ResourceManifestPtr manifest = gResources().getResourceManifest();
|
|
|
+ manifest->registerResource(importedResource.getUUID(), internalResourcesPath);
|
|
|
+
|
|
|
resource->lastUpdateTime = std::time(nullptr);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void ProjectLibrary::deleteResource(ResourceEntry* resource)
|
|
|
+ bool ProjectLibrary::isUpToDate(ResourceEntry* resource) const
|
|
|
{
|
|
|
+ if(resource->meta == nullptr)
|
|
|
+ return false;
|
|
|
+
|
|
|
ResourceManifestPtr manifest = gResources().getResourceManifest();
|
|
|
|
|
|
- if(resource->meta != nullptr)
|
|
|
- {
|
|
|
- if(manifest->uuidExists(resource->meta->getUUID()))
|
|
|
- {
|
|
|
- const WString& path = manifest->uuidToFilePath(resource->meta->getUUID());
|
|
|
- if(FileSystem::isFile(path))
|
|
|
- FileSystem::remove(path);
|
|
|
- }
|
|
|
+ if(!manifest->uuidExists(resource->meta->getUUID()))
|
|
|
+ return false;
|
|
|
|
|
|
- WString ext = toWString(PathUtil::getExtension(resource->path));
|
|
|
- WPath metaPath = resource->path;
|
|
|
- PathUtil::replaceExtension(metaPath, ext + L".meta");
|
|
|
+ const WPath& path = manifest->uuidToFilePath(resource->meta->getUUID());
|
|
|
+ if(!FileSystem::isFile(path))
|
|
|
+ return false;
|
|
|
|
|
|
- if(FileSystem::isFile(metaPath))
|
|
|
- FileSystem::remove(metaPath);
|
|
|
+ std::time_t lastModifiedTime = FileSystem::getLastModifiedTime(path);
|
|
|
+ return lastModifiedTime <= resource->lastUpdateTime;
|
|
|
+ }
|
|
|
+
|
|
|
+ ProjectLibrary::LibraryEntry* ProjectLibrary::findEntry(const CM::WPath& fullPath) const
|
|
|
+ {
|
|
|
+ auto pathIter = fullPath.begin();
|
|
|
+ auto rootIter = mRootEntry->path.begin();
|
|
|
+
|
|
|
+ while(*pathIter == *rootIter)
|
|
|
+ {
|
|
|
+ ++pathIter;
|
|
|
+ ++rootIter;
|
|
|
}
|
|
|
|
|
|
- DirectoryEntry* parent = resource->parent;
|
|
|
- auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
|
|
|
- [&] (const LibraryEntry* entry) { return entry == resource; });
|
|
|
+ if(pathIter == fullPath.begin()) // Not a single entry matches Resources path
|
|
|
+ return nullptr;
|
|
|
|
|
|
- parent->mChildren.erase(findIter);
|
|
|
+ --pathIter;
|
|
|
|
|
|
- cm_delete(resource);
|
|
|
+ Stack<LibraryEntry*>::type todo;
|
|
|
+ todo.push(mRootEntry);
|
|
|
+
|
|
|
+ while(!todo.empty())
|
|
|
+ {
|
|
|
+ LibraryEntry* current = todo.top();
|
|
|
+ todo.pop();
|
|
|
+
|
|
|
+ if(*pathIter == *(--(current->path.end())))
|
|
|
+ {
|
|
|
+ if(current->type == LibraryEntryType::Directory)
|
|
|
+ {
|
|
|
+ DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(current);
|
|
|
+ for(auto& child : dirEntry->mChildren)
|
|
|
+ todo.push(child);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(pathIter == fullPath.end())
|
|
|
+ return current;
|
|
|
+
|
|
|
+ ++pathIter;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nullptr;
|
|
|
}
|
|
|
|
|
|
- void ProjectLibrary::deleteDirectory(DirectoryEntry* directory)
|
|
|
+ void ProjectLibrary::moveEntry(const CM::WPath& oldPath, const CM::WPath& newPath)
|
|
|
{
|
|
|
- if(directory == mRootEntry)
|
|
|
- mRootEntry = nullptr;
|
|
|
+ if(FileSystem::isFile(oldPath) || FileSystem::isDirectory(oldPath))
|
|
|
+ FileSystem::move(oldPath, newPath);
|
|
|
|
|
|
- for(auto& child : directory->mChildren)
|
|
|
+ WPath oldMetaPath = getMetaPath(oldPath);
|
|
|
+ WPath newMetaPath = getMetaPath(newPath);
|
|
|
+
|
|
|
+ LibraryEntry* oldEntry = findEntry(oldPath);
|
|
|
+ if(oldEntry != nullptr) // Moving from the Resources folder
|
|
|
{
|
|
|
- if(child->type == LibraryEntryType::Directory)
|
|
|
- deleteDirectory(static_cast<DirectoryEntry*>(child));
|
|
|
- else
|
|
|
- deleteResource(static_cast<ResourceEntry*>(child));
|
|
|
- }
|
|
|
+ // Moved outside of Resources, delete entry & meta file
|
|
|
+ if(!PathUtil::includes(toWString(newPath), EditorApplication::instance().getResourcesFolderPath()))
|
|
|
+ {
|
|
|
+ if(oldEntry->type == LibraryEntryType::File)
|
|
|
+ {
|
|
|
+ deleteResourceInternal(static_cast<ResourceEntry*>(oldEntry));
|
|
|
|
|
|
- DirectoryEntry* parent = directory->parent;
|
|
|
- auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
|
|
|
- [&] (const LibraryEntry* entry) { return entry == directory; });
|
|
|
+ if(FileSystem::isFile(oldMetaPath))
|
|
|
+ FileSystem::remove(oldMetaPath);
|
|
|
+ }
|
|
|
+ else if(oldEntry->type == LibraryEntryType::Directory)
|
|
|
+ deleteDirectoryInternal(static_cast<DirectoryEntry*>(oldEntry));
|
|
|
+ }
|
|
|
+ else // Just moving internally
|
|
|
+ {
|
|
|
+ if(FileSystem::isFile(oldMetaPath))
|
|
|
+ FileSystem::move(oldMetaPath, newMetaPath);
|
|
|
|
|
|
- parent->mChildren.erase(findIter);
|
|
|
+ DirectoryEntry* parent = oldEntry->parent;
|
|
|
+ auto findIter = std::find(parent->mChildren.begin(), parent->mChildren.end(), oldEntry);
|
|
|
+ if(findIter != parent->mChildren.end())
|
|
|
+ parent->mChildren.erase(findIter);
|
|
|
|
|
|
- cm_delete(directory);
|
|
|
- }
|
|
|
+ WPath parentPath = PathUtil::parentPath(newPath);
|
|
|
|
|
|
- bool ProjectLibrary::isUpToDate(ResourceEntry* resource) const
|
|
|
- {
|
|
|
- if(resource->meta == nullptr)
|
|
|
- return false;
|
|
|
+ DirectoryEntry* newEntryParent = nullptr;
|
|
|
+ LibraryEntry* newEntryParentLib = findEntry(parentPath);
|
|
|
+ if(newEntryParentLib != nullptr)
|
|
|
+ {
|
|
|
+ assert(newEntryParentLib->type == LibraryEntryType::Directory);
|
|
|
+ newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
|
|
|
+ }
|
|
|
|
|
|
- ResourceManifestPtr manifest = gResources().getResourceManifest();
|
|
|
+ DirectoryEntry* newHierarchyParent = nullptr;
|
|
|
+ if(newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
|
|
|
+ createInternalParentHierarchy(newPath, &newHierarchyParent, &newEntryParent);
|
|
|
|
|
|
- if(!manifest->uuidExists(resource->meta->getUUID()))
|
|
|
- return false;
|
|
|
+ newEntryParent->mChildren.push_back(oldEntry);
|
|
|
+ oldEntry->parent = newEntryParent;
|
|
|
+ oldEntry->path = newPath;
|
|
|
|
|
|
- const WString& path = manifest->uuidToFilePath(resource->meta->getUUID());
|
|
|
- if(!FileSystem::isFile(path))
|
|
|
- return false;
|
|
|
+ if(!onEntryRemoved.empty())
|
|
|
+ onEntryRemoved(oldPath);
|
|
|
|
|
|
- std::time_t lastModifiedTime = FileSystem::getLastModifiedTime(path);
|
|
|
- return lastModifiedTime <= resource->lastUpdateTime;
|
|
|
+ if(!onEntryAdded.empty())
|
|
|
+ onEntryAdded(newPath);
|
|
|
+
|
|
|
+ if(newHierarchyParent != nullptr)
|
|
|
+ checkForModifications(toWString(newHierarchyParent->path));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else // Moving from outside of the Resources folder (likely adding a new resource)
|
|
|
+ {
|
|
|
+ checkForModifications(toWString(newPath));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- ProjectLibrary::DirectoryEntry* ProjectLibrary::findDirectoryEntry(const CM::WString& folder) const
|
|
|
+ void ProjectLibrary::deleteEntry(const CM::WPath& path)
|
|
|
{
|
|
|
- WPath pathToSearch = toPath(folder);
|
|
|
+ if(FileSystem::isFile(path))
|
|
|
+ FileSystem::remove(path);
|
|
|
|
|
|
- Stack<DirectoryEntry*>::type todo;
|
|
|
- todo.push(mRootEntry);
|
|
|
+ LibraryEntry* entry = findEntry(path);
|
|
|
+ if(entry != nullptr)
|
|
|
+ {
|
|
|
+ if(entry->type == LibraryEntryType::File)
|
|
|
+ {
|
|
|
+ deleteResourceInternal(static_cast<ResourceEntry*>(entry));
|
|
|
|
|
|
- while(!todo.empty())
|
|
|
+ WPath metaPath = getMetaPath(path);
|
|
|
+ if(FileSystem::isFile(metaPath))
|
|
|
+ FileSystem::remove(metaPath);
|
|
|
+ }
|
|
|
+ else if(entry->type == LibraryEntryType::Directory)
|
|
|
+ deleteDirectoryInternal(static_cast<DirectoryEntry*>(entry));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void ProjectLibrary::createInternalParentHierarchy(const CM::WPath& fullPath, DirectoryEntry** newHierarchyRoot, DirectoryEntry** newHierarchyLeaf)
|
|
|
+ {
|
|
|
+ WPath parentPath = fullPath;
|
|
|
+
|
|
|
+ DirectoryEntry* newEntryParent = nullptr;
|
|
|
+ Stack<WPath>::type parentPaths;
|
|
|
+ do
|
|
|
{
|
|
|
- DirectoryEntry* currentDir = todo.top();
|
|
|
- todo.pop();
|
|
|
+ WPath newParentPath = PathUtil::parentPath(parentPath);
|
|
|
+
|
|
|
+ if(newParentPath == parentPath)
|
|
|
+ break;
|
|
|
|
|
|
- for(auto& child : currentDir->mChildren)
|
|
|
+ LibraryEntry* newEntryParentLib = findEntry(newParentPath);
|
|
|
+ if(newEntryParentLib != nullptr)
|
|
|
{
|
|
|
- if(child->type == LibraryEntryType::Directory)
|
|
|
- {
|
|
|
- if(pathToSearch == child->path)
|
|
|
- return static_cast<DirectoryEntry*>(child);
|
|
|
+ assert(newEntryParentLib->type == LibraryEntryType::Directory);
|
|
|
+ newEntryParent = static_cast<DirectoryEntry*>(newEntryParentLib);
|
|
|
|
|
|
- todo.push(static_cast<DirectoryEntry*>(child));
|
|
|
- }
|
|
|
+ break;
|
|
|
}
|
|
|
+
|
|
|
+ parentPaths.push(newParentPath);
|
|
|
+ parentPath = newParentPath;
|
|
|
+
|
|
|
+ } while (true);
|
|
|
+
|
|
|
+ assert(newEntryParent != nullptr); // Must exist
|
|
|
+
|
|
|
+ if(newHierarchyRoot != nullptr)
|
|
|
+ *newHierarchyRoot = newEntryParent;
|
|
|
+
|
|
|
+ while(!parentPaths.empty())
|
|
|
+ {
|
|
|
+ WPath curPath = parentPaths.top();
|
|
|
+ parentPaths.pop();
|
|
|
+
|
|
|
+ newEntryParent = addDirectoryInternal(newEntryParent, curPath);
|
|
|
}
|
|
|
|
|
|
- return nullptr;
|
|
|
+ if(newHierarchyLeaf != nullptr)
|
|
|
+ *newHierarchyLeaf = newEntryParent;
|
|
|
+ }
|
|
|
+
|
|
|
+ WPath ProjectLibrary::getMetaPath(const CM::WPath& path) const
|
|
|
+ {
|
|
|
+ WString ext = toWString(PathUtil::getExtension(path));
|
|
|
+ WPath metaPath = path;
|
|
|
+ PathUtil::replaceExtension(metaPath, ext + L".meta");
|
|
|
+
|
|
|
+ return metaPath;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool ProjectLibrary::isMeta(const WPath& fullPath) const
|
|
|
+ {
|
|
|
+ return PathUtil::getExtension(fullPath) == toPath(L".meta");
|
|
|
+ }
|
|
|
+
|
|
|
+ void ProjectLibrary::onMonitorFileModified(const WString& path)
|
|
|
+ {
|
|
|
+ checkForModifications(path);
|
|
|
}
|
|
|
}
|