diff --git a/engine/includes/adapters/handlers/defaultfilehandler.h b/engine/includes/adapters/handlers/defaultfilehandler.h index a404c4e1f..dbd475aa7 100644 --- a/engine/includes/adapters/handlers/defaultfilehandler.h +++ b/engine/includes/adapters/handlers/defaultfilehandler.h @@ -46,7 +46,11 @@ class DefaultFileHandler : public FileHandler { bool remove(const char *path) override { try { - return std::filesystem::remove(path); + if(isDir(path)) { + return std::filesystem::remove_all(path); + } else { + return std::filesystem::remove(path); + } } catch (const std::filesystem::filesystem_error &) { return false; } diff --git a/engine/includes/editor/baseassetprovider.h b/engine/includes/editor/baseassetprovider.h index 2f326fa17..9fafe0da9 100644 --- a/engine/includes/editor/baseassetprovider.h +++ b/engine/includes/editor/baseassetprovider.h @@ -1,40 +1,37 @@ #ifndef BASEASSETPROVIDER_H #define BASEASSETPROVIDER_H -#include - #include -class QFileSystemWatcher; +class FileSystemWatcher; + +class ENGINE_EXPORT BaseAssetProvider : public Object { + A_OBJECT(BaseAssetProvider, Object, Core) + + A_METHODS( + A_SLOT(BaseAssetProvider::onFileChanged), + A_SLOT(BaseAssetProvider::onDirectoryChanged) + ) -class ENGINE_EXPORT BaseAssetProvider : public QObject { - Q_OBJECT public: BaseAssetProvider(); - ~BaseAssetProvider(); - void init(); + void init(bool force); void renameResource(const TString &source, const TString &destination); void removeResource(const TString &source); void duplicateResource(const TString &source); - void cleanupBundle(); - -protected: - bool copyRecursively(const TString &sourceFolder, const TString &destFolder); - -public slots: - void onFileChanged(const QString &path); - void onFileChangedForce(const QString &path, bool force = false); +public: // slots + void onFileChanged(const TString &path); + void onFileChangedForce(const TString &path, bool force = false); - void onDirectoryChanged(const QString &path); - void onDirectoryChangedForce(const QString &path, bool force = false); + void onDirectoryChanged(const TString &path); + void onDirectoryChangedForce(const TString &path, bool force = false); private: - QFileSystemWatcher *m_dirWatcher; - QFileSystemWatcher *m_fileWatcher; + FileSystemWatcher *m_dirWatcher; }; diff --git a/engine/src/editor/assetmanager.cpp b/engine/src/editor/assetmanager.cpp index 4f866a729..f97e7be6f 100644 --- a/engine/src/editor/assetmanager.cpp +++ b/engine/src/editor/assetmanager.cpp @@ -119,7 +119,6 @@ void AssetManager::rescan() { TString target = m_projectManager->targetPath(); if(target.isEmpty()) { - bool update = m_projectManager->projectSdk() != SDK_VERSION; if(update) { getChangedUUIDs(); @@ -128,24 +127,11 @@ void AssetManager::rescan() { Engine::resourceSystem()->unloadBundle(TString()); m_force |= !Engine::resourceSystem()->loadBundle(TString()); m_force |= update; - - m_assetProvider->init(); } else { m_force = true; } - m_assetProvider->onDirectoryChangedForce((m_projectManager->resourcePath() + "/engine/materials").data(),m_force); - m_assetProvider->onDirectoryChangedForce((m_projectManager->resourcePath() + "/engine/textures").data(), m_force); - m_assetProvider->onDirectoryChangedForce((m_projectManager->resourcePath() + "/engine/meshes").data(), m_force); - m_assetProvider->onDirectoryChangedForce((m_projectManager->resourcePath() + "/engine/pipelines").data(),m_force); - m_assetProvider->onDirectoryChangedForce((m_projectManager->resourcePath() + "/engine/fonts").data(), m_force); -#ifndef BUILDER - m_assetProvider->onDirectoryChangedForce((m_projectManager->resourcePath() + "/editor/materials").data(),m_force); - m_assetProvider->onDirectoryChangedForce((m_projectManager->resourcePath() + "/editor/gizmos").data(), m_force); - m_assetProvider->onDirectoryChangedForce((m_projectManager->resourcePath() + "/editor/meshes").data(), m_force); - m_assetProvider->onDirectoryChangedForce((m_projectManager->resourcePath() + "/editor/textures").data(), m_force); -#endif - m_assetProvider->onDirectoryChangedForce(m_projectManager->contentPath().data(), m_force); + m_assetProvider->init(m_force); Engine::resourceSystem()->setCleanImport(m_force); @@ -175,7 +161,7 @@ TString AssetManager::assetTypeName(const TString &source) { } bool AssetManager::pushToImport(const TString &source) { - m_assetProvider->onFileChangedForce(source.data(), true); + m_assetProvider->onFileChangedForce(source, true); return true; } @@ -601,7 +587,13 @@ void AssetManager::onPerform() { } } - m_assetProvider->cleanupBundle(); + // Cleanup bundle + for(auto &path : File::list(ProjectSettings::instance()->importPath())) { + TString fileName(Url(path).name()); + if(!File::isDir(path) && fileName != gIndex && uuidToPath(fileName).isEmpty()) { + File::remove(path); + } + } auto tmp = m_indices; for(auto &index : tmp) { diff --git a/engine/src/editor/baseassetprovider.cpp b/engine/src/editor/baseassetprovider.cpp index e4745cba0..ef330e914 100644 --- a/engine/src/editor/baseassetprovider.cpp +++ b/engine/src/editor/baseassetprovider.cpp @@ -1,7 +1,6 @@ #include "editor/baseassetprovider.h" -#include -#include +#include #include "editor/projectsettings.h" #include "editor/assetmanager.h" @@ -10,60 +9,62 @@ #include "config.h" BaseAssetProvider::BaseAssetProvider() : - m_dirWatcher(new QFileSystemWatcher(this)), - m_fileWatcher(new QFileSystemWatcher(this)) { + m_dirWatcher(nullptr) { + FileSystemWatcher::registerClassFactory(&Engine::instance()); + + m_dirWatcher = Engine::objectCreate(); + + connect(m_dirWatcher, _SIGNAL(directoryChanged(TString)), this, _SLOT(onDirectoryChanged(TString))); + connect(m_dirWatcher, _SIGNAL(fileChanged(TString)), this, _SLOT(onFileChanged(TString))); } BaseAssetProvider::~BaseAssetProvider() { delete m_dirWatcher; - delete m_fileWatcher; } -void BaseAssetProvider::init() { - QStringList paths = m_dirWatcher->directories(); - if(!paths.isEmpty()) { +void BaseAssetProvider::init(bool force) { + StringList paths = m_dirWatcher->directories(); + if(!paths.empty()) { m_dirWatcher->removePaths(paths); } - connect(m_dirWatcher, &QFileSystemWatcher::directoryChanged, this, &BaseAssetProvider::onDirectoryChanged); - connect(m_dirWatcher, SIGNAL(directoryChanged(QString)), AssetManager::instance(), SIGNAL(directoryChanged(QString))); - connect(m_dirWatcher, SIGNAL(directoryChanged(QString)), AssetManager::instance(), SLOT(reimport())); - - connect(m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &BaseAssetProvider::onFileChanged); - connect(m_fileWatcher, &QFileSystemWatcher::fileChanged, AssetManager::instance(), &AssetManager::fileChanged); - connect(m_fileWatcher, &QFileSystemWatcher::fileChanged, AssetManager::instance(), &AssetManager::reimport); + ProjectSettings *mgr = ProjectSettings::instance(); + TString resourcePath(ProjectSettings::instance()->resourcePath()); + + onDirectoryChangedForce(resourcePath + "/engine/materials",force); + onDirectoryChangedForce(resourcePath + "/engine/textures", force); + onDirectoryChangedForce(resourcePath + "/engine/meshes", force); + onDirectoryChangedForce(resourcePath + "/engine/pipelines",force); + onDirectoryChangedForce(resourcePath + "/engine/fonts", force); +#ifndef BUILDER + onDirectoryChangedForce(resourcePath + "/editor/materials",force); + onDirectoryChangedForce(resourcePath + "/editor/gizmos", force); + onDirectoryChangedForce(resourcePath + "/editor/meshes", force); + onDirectoryChangedForce(resourcePath + "/editor/textures", force); +#endif + onDirectoryChangedForce(mgr->contentPath(), force); } -void BaseAssetProvider::cleanupBundle() { - AssetManager *mgr = AssetManager::instance(); - - for(auto &path : File::list(ProjectSettings::instance()->importPath())) { - TString fileName(Url(path).name()); - if(!File::isDir(path) && fileName != gIndex && mgr->uuidToPath(fileName).isEmpty()) { - File::remove(path); - } - } -} - -void BaseAssetProvider::onFileChanged(const QString &path) { +void BaseAssetProvider::onFileChanged(const TString &path) { onFileChangedForce(path); -} -void BaseAssetProvider::onFileChangedForce(const QString &path, bool force) { - AssetManager *mgr = AssetManager::instance(); + AssetManager::instance()->fileChanged(path.data()); + AssetManager::instance()->reimport(); +} - TString filePath(path.toStdString()); - if(File::exists(filePath) && Url(path.toStdString()).suffix() != gMetaExt) { - AssetConverterSettings *settings = mgr->fetchSettings(filePath); +void BaseAssetProvider::onFileChangedForce(const TString &path, bool force) { + if(File::exists(path) && Url(path).suffix() != gMetaExt) { + AssetManager *mgr = AssetManager::instance(); + AssetConverterSettings *settings = mgr->fetchSettings(path); if(settings) { if(force || settings->isOutdated()) { mgr->pushToImport(settings); } else { if(!settings->isCode()) { - mgr->registerAsset(filePath, settings->info()); + mgr->registerAsset(path, settings->info()); for(const TString &it : settings->subKeys()) { - mgr->registerAsset(filePath + "/" + it, settings->subItem(it)); + mgr->registerAsset(path + "/" + it, settings->subItem(it)); } } } @@ -71,26 +72,24 @@ void BaseAssetProvider::onFileChangedForce(const QString &path, bool force) { } } -void BaseAssetProvider::onDirectoryChanged(const QString &path) { +void BaseAssetProvider::onDirectoryChanged(const TString &path) { onDirectoryChangedForce(path); + + AssetManager::instance()->directoryChanged(path.data()); } -void BaseAssetProvider::onDirectoryChangedForce(const QString &path, bool force) { +void BaseAssetProvider::onDirectoryChangedForce(const TString &path, bool force) { m_dirWatcher->addPath(path); - for(auto &item : File::list(path.toStdString())) { + for(auto &item : File::list(path)) { if(Url(item).suffix() == gMetaExt) { continue; } - if(File::isDir(item)) { - m_dirWatcher->addPath(item.data()); - continue; + m_dirWatcher->addPath(item); + } else { + onFileChangedForce(item, force); } - - m_fileWatcher->addPath(item.data()); - - onFileChangedForce(item.data(), force); } } @@ -100,26 +99,9 @@ void BaseAssetProvider::removeResource(const TString &source) { } ProjectSettings *project = ProjectSettings::instance(); + AssetManager *asset = AssetManager::instance(); TString src(project->contentPath() + "/" + source); - if(File::isDir(src)) { - m_dirWatcher->removePath(src.data()); - QDir dir(project->contentPath().data()); - QDirIterator it(src.data(), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); - while(it.hasNext()) { - removeResource(dir.relativeFilePath(it.next()).toStdString()); - } - QDir().rmdir(src.data()); - return; - } - - AssetManager *asset = AssetManager::instance(); - CodeBuilder *builder = nullptr; - BuilderSettings *settings = dynamic_cast(asset->fetchSettings(src)); - if(settings) { - builder = settings->builder(); - } - m_fileWatcher->removePath(src.data()); Engine::unloadResource(source); TString uuid = asset->unregisterAsset(source); @@ -131,6 +113,11 @@ void BaseAssetProvider::removeResource(const TString &source) { File::remove(src + "." + gMetaExt); File::remove(src); + CodeBuilder *builder = nullptr; + BuilderSettings *settings = dynamic_cast(asset->fetchSettings(src)); + if(settings) { + builder = settings->builder(); + } if(builder) { builder->rescanSources(project->contentPath()); if(!builder->isEmpty()) { @@ -144,29 +131,26 @@ void BaseAssetProvider::removeResource(const TString &source) { void BaseAssetProvider::renameResource(const TString &oldName, const TString &newName) { AssetManager *asset = AssetManager::instance(); - ProjectSettings *project = ProjectSettings::instance(); ResourceSystem::Dictionary &indices(Engine::resourceSystem()->indices()); if(File::isDir(oldName)) { - QStringList dirs = m_dirWatcher->directories(); - QStringList files = m_fileWatcher->files(); - if(!dirs.isEmpty()) { + StringList dirs = m_dirWatcher->directories(); + if(!dirs.empty()) { m_dirWatcher->removePaths(dirs); - m_dirWatcher->addPaths(dirs.replaceInStrings(oldName.data(), newName.data())); - } - if(!files.isEmpty()) { - m_fileWatcher->removePaths(files); - m_fileWatcher->addPaths(files.replaceInStrings(oldName.data(), newName.data())); + for(auto &it : dirs) { + it.replace(oldName, newName); + } + m_dirWatcher->addPaths(dirs); } - QDir dir; - if(dir.rename(oldName.data(), newName.data())) { + if(File::rename(oldName, newName)) { std::map back; + ProjectSettings *project = ProjectSettings::instance(); for(auto it = indices.cbegin(); it != indices.cend();) { - QString path = (project->contentPath() + "/" + it->first).data(); - if(path.startsWith(oldName.data())) { + TString path(project->contentPath() + "/" + it->first); + if(path.startsWith(oldName)) { back[path.toStdString()] = it->second; it = indices.erase(it); } else { @@ -181,12 +165,9 @@ void BaseAssetProvider::renameResource(const TString &oldName, const TString &ne } asset->dumpBundle(); } else { - if(!dirs.isEmpty()) { + if(!dirs.empty()) { m_dirWatcher->addPaths(dirs); } - if(!files.isEmpty()) { - m_fileWatcher->addPaths(files); - } } } else { if(File::rename(oldName, newName) && @@ -203,7 +184,7 @@ void BaseAssetProvider::renameResource(const TString &oldName, const TString &ne AssetConverterSettings *settings = asset->fetchSettings(newName); if(settings) { AssetConverter *converter = asset->getConverter(newName); - converter->renameAsset(settings, Url(oldName).baseName(), Url(oldName).baseName()); + converter->renameAsset(settings, Url(oldName).baseName(), Url(newName).baseName()); } } } @@ -211,9 +192,8 @@ void BaseAssetProvider::renameResource(const TString &oldName, const TString &ne void BaseAssetProvider::duplicateResource(const TString &source) { AssetManager *asset = AssetManager::instance(); - ProjectSettings *project = ProjectSettings::instance(); - TString src(project->contentPath() + "/" + source); + TString src(ProjectSettings::instance()->contentPath() + "/" + source); Url info(src); @@ -250,38 +230,3 @@ void BaseAssetProvider::duplicateResource(const TString &source) { asset->dumpBundle(); } } - -// Copied from: https://forum.qt.io/topic/59245/is-there-any-api-to-recursively-copy-a-directory-and-all-it-s-sub-dirs-and-files/3 -bool BaseAssetProvider::copyRecursively(const TString &sourceFolder, const TString &destFolder) { - QDir sourceDir(sourceFolder.data()); - - if(!sourceDir.exists()) { - return false; - } - - QDir destDir(destFolder.data()); - if(!destDir.exists()) { - destDir.mkdir(destFolder.data()); - } - - QStringList files = sourceDir.entryList(QDir::Files); - for(int i = 0; i< files.count(); i++) { - TString srcName = sourceFolder + "/" + files[i].toStdString(); - TString destName = destFolder + "/" + files[i].toStdString(); - if(!QFile::copy(srcName.data(), destName.data())) { - return false; - } - } - - files.clear(); - files = sourceDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); - for(int i = 0; i < files.count(); i++) { - TString srcName = sourceFolder + "/" + files[i].toStdString(); - TString destName = destFolder + "/" + files[i].toStdString(); - if(!copyRecursively(srcName, destName)) { - return false; - } - } - - return true; -} diff --git a/thirdparty/next/inc/core/astring.h b/thirdparty/next/inc/core/astring.h index 778752a85..32dd704f0 100644 --- a/thirdparty/next/inc/core/astring.h +++ b/thirdparty/next/inc/core/astring.h @@ -80,6 +80,8 @@ class NEXT_LIBRARY_EXPORT TString { bool isEmpty() const; + bool startsWith(const TString &str) const; + int indexOf(const TString &str) const; int indexOf(const char ch) const; diff --git a/thirdparty/next/inc/core/objectsystem.h b/thirdparty/next/inc/core/objectsystem.h index bd6403ff6..6a9ab9e34 100644 --- a/thirdparty/next/inc/core/objectsystem.h +++ b/thirdparty/next/inc/core/objectsystem.h @@ -89,6 +89,8 @@ class NEXT_LIBRARY_EXPORT ObjectSystem : public Object { static void removeInvalid(Invalid *invalid); + static void notify(Object *receiver, Event *event); + protected: virtual void factoryAdd(const TString &name, const TString &url, const MetaObject *meta); diff --git a/thirdparty/next/inc/os/filesystemwatcher.h b/thirdparty/next/inc/os/filesystemwatcher.h new file mode 100644 index 000000000..2dcbbf60b --- /dev/null +++ b/thirdparty/next/inc/os/filesystemwatcher.h @@ -0,0 +1,60 @@ +/* + This file is part of Thunder Next. + + Thunder Next is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + + Thunder Next is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Thunder Next. If not, see . + + Copyright: 2008-2026 Evgeniy Prikazchikov +*/ + +#ifndef FILESYSTEMWATCHER_H +#define FILESYSTEMWATCHER_H + +#include + +class FileSystemWatcherPrivate; + +class NEXT_LIBRARY_EXPORT FileSystemWatcher : public Object { + A_OBJECT(FileSystemWatcher, Object, Core) + + A_METHODS( + A_SIGNAL(FileSystemWatcher::fileChanged), + A_SIGNAL(FileSystemWatcher::directoryChanged) + ) + +public: + FileSystemWatcher(); + ~FileSystemWatcher(); + + bool addPath(const TString &path); + bool addPaths(const StringList &paths); + + bool removePath(const TString &path); + bool removePaths(const StringList &paths); + + StringList directories() const; + StringList files() const; + +public: // signals + void fileChanged(const TString &path); + + void directoryChanged(const TString &path); + +private: + bool event(Event *event) override; + +private: + FileSystemWatcherPrivate *m_ptr; + +}; + +#endif // FILESYSTEMWATCHER_H diff --git a/thirdparty/next/src/core/astring.cpp b/thirdparty/next/src/core/astring.cpp index 906c9902d..5580ad3a2 100644 --- a/thirdparty/next/src/core/astring.cpp +++ b/thirdparty/next/src/core/astring.cpp @@ -211,6 +211,16 @@ TString TString::join(const StringList &list, const char *separator) { bool TString::isEmpty() const { return m_data.empty(); } +/*! + Returns true if this string starts with the string \a str; otherwise returns false. + The comparison is case-sensitive. +*/ +bool TString::startsWith(const TString &str) const { + if (str.length() > length()) { + return false; + } + return m_data.compare(0, str.length(), str.m_data) == 0; +} /*! Returns the index position of the first occurrence of the string \a str in this string. Returns -1 if \a str is not found. */ diff --git a/thirdparty/next/src/core/objectsystem.cpp b/thirdparty/next/src/core/objectsystem.cpp index 662484543..6d5a7d075 100644 --- a/thirdparty/next/src/core/objectsystem.cpp +++ b/thirdparty/next/src/core/objectsystem.cpp @@ -556,7 +556,12 @@ Object::ObjectList &ObjectSystem::invalidObjects() { void ObjectSystem::removeInvalid(Invalid *invalid) { s_Invalids.remove(invalid); } - +/*! + Sends \a event to \a receiver. +*/ +void ObjectSystem::notify(Object *receiver, Event *event) { + receiver->postEvent(event); +} /*! Returns a list of objects with specified \a type. \warning This is very small function! diff --git a/thirdparty/next/src/os/filesystemwatcher.cpp b/thirdparty/next/src/os/filesystemwatcher.cpp new file mode 100644 index 000000000..a11252432 --- /dev/null +++ b/thirdparty/next/src/os/filesystemwatcher.cpp @@ -0,0 +1,230 @@ +#include "os/filesystemwatcher.h" + +#include +#include + +#include +#include +#include +#include +#include + +class FileSystemWatcherEvent : public Event { +public: + FileSystemWatcherEvent(uint32_t type, const TString &path) : + Event(type), + m_path(path) { + + } + + TString m_path; + +}; + +class FileSystemWatcherPrivate { +public: + FileSystemWatcherPrivate(FileSystemWatcher *ptr) : + running(false), + p_ptr(ptr) { + + } + + bool addPath(const TString &path) { + if(!File::exists(path)) { + return false; + } + { + std::lock_guard lock(mutex); + if(std::find(paths.begin(), paths.end(), path) != paths.end()) { + return true; + } + + paths.push_back(path); + lastWriteTime[path] = getLastWriteTime(path); + } + + start(); + + return true; + } + + bool removePath(const TString &path) { + std::lock_guard lock(mutex); + + auto it = std::find(paths.begin(), paths.end(), path); + if(it != paths.end()) { + paths.erase(it); + lastWriteTime.erase(path); + + return true; + } + return false; + } + + static std::filesystem::file_time_type getLastWriteTime(const TString &path) { + try { + if(File::exists(path)) { + return std::filesystem::last_write_time(path.toStdString()); + } + } catch (...) {} + return std::filesystem::file_time_type(); + } + + void start() { + if(running) { + return; + } + + running = true; + watcherThread = std::thread(&FileSystemWatcherPrivate::pollingLoop, this); + } + + void stop() { + if(!running) { + return; + } + + running = false; + + if(watcherThread.joinable()) { + watcherThread.join(); + } + } + + void checkDirectory(const TString &dirPath) { + try { + if(!File::exists(dirPath)) { + { + std::lock_guard lock(mutex); + paths.remove(dirPath); + } + TString path(Url(dirPath).absoluteDir()); + ObjectSystem::notify(p_ptr, new FileSystemWatcherEvent(Event::UserType, path)); + return; + } + + for(const auto &entry : File::list(dirPath)) { + try { + auto currentTime = getLastWriteTime(entry); + auto lastTime = lastWriteTime[entry]; + + if(currentTime > lastTime) { + ObjectSystem::notify(p_ptr, new FileSystemWatcherEvent(Event::UserType, entry)); + lastWriteTime[entry] = currentTime; + } + } catch (...) {} + } + } catch (const std::filesystem::filesystem_error &e) {} + } + + void pollingLoop() { + StringList pathsCopy; + while(running) { + { + std::lock_guard lock(mutex); + pathsCopy = paths; + } + + for(const auto &path : pathsCopy) { + checkDirectory(path); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + } + +public: + StringList paths; + std::unordered_map lastWriteTime; + std::thread watcherThread; + std::mutex mutex; + + bool running; + + FileSystemWatcher *p_ptr = nullptr; +}; + +FileSystemWatcher::FileSystemWatcher() : + m_ptr(new FileSystemWatcherPrivate(this)) { + +} + +FileSystemWatcher::~FileSystemWatcher() { + m_ptr->stop(); + delete m_ptr; +} + +bool FileSystemWatcher::addPath(const TString &path) { + return m_ptr->addPath(path); +} + +bool FileSystemWatcher::addPaths(const StringList &paths) { + bool allSuccess = true; + for(const auto &path : paths) { + if(!addPath(path)) { + allSuccess = false; + } + } + return allSuccess; +} + +bool FileSystemWatcher::removePath(const TString &path) { + return m_ptr->removePath(path); +} + +bool FileSystemWatcher::removePaths(const StringList &paths) { + bool allSuccess = true; + for(const auto &path : paths) { + if(!removePath(path)) { + allSuccess = false; + } + } + return allSuccess; +} + +StringList FileSystemWatcher::directories() const { + StringList result; + + for(auto &it : m_ptr->paths) { + if(File::isDir(it)) { + result.push_back(it); + } + } + + return result; +} + +StringList FileSystemWatcher::files() const { + StringList result; + + for(auto &it : m_ptr->paths) { + if(File::isFile(it)) { + result.push_back(it); + } + } + + return result; +} + +void FileSystemWatcher::fileChanged(const TString &path) { + emitSignal(_SIGNAL(fileChanged(TString)), path); +} + +void FileSystemWatcher::directoryChanged(const TString &path) { + emitSignal(_SIGNAL(directoryChanged(TString)), path); +} + +bool FileSystemWatcher::event(Event *event) { + FileSystemWatcherEvent *ev = dynamic_cast(event); + if(ev) { + TString path = ev->m_path; + if(File::isDir(path)) { // Directory modified + directoryChanged(path); + } else { // File modified + fileChanged(path); + } + return true; + } + + return false; +}