From 5c59821ac8e885c1cd65937fe530207f70844b81 Mon Sep 17 00:00:00 2001 From: Hugues Delorme Date: Tue, 30 Jul 2024 18:43:30 +0200 Subject: [PATCH] App: fix monitoring of Document files Relates to GitHub #279 --- src/app/commands_file.cpp | 7 ++--- src/app/document_files_watcher.cpp | 45 ++++++++++++++++++++++++++---- src/app/document_files_watcher.h | 7 +++++ tests/test_app.cpp | 13 +++++++-- 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/src/app/commands_file.cpp b/src/app/commands_file.cpp index 013c035b..d716313f 100644 --- a/src/app/commands_file.cpp +++ b/src/app/commands_file.cpp @@ -248,15 +248,14 @@ void FileCommandTools::importInDocument( const QString taskTitle = listFilePaths.size() > 1 ? Command::tr("Import") : - filepathTo(listFilePaths.front().stem()); + filepathTo(listFilePaths.front().stem()) + ; context->taskMgr()->setTitle(taskId, to_stdString(taskTitle)); context->taskMgr()->run(taskId); } void FileCommandTools::importInDocument( - IAppContext* context, - const DocumentPtr& targetDoc, - const FilePath& filePath + IAppContext* context, const DocumentPtr& targetDoc, const FilePath& filePath ) { FileCommandTools::importInDocument(context, targetDoc, Span(&filePath, 1)); diff --git a/src/app/document_files_watcher.cpp b/src/app/document_files_watcher.cpp index f57477e6..04d13dd1 100644 --- a/src/app/document_files_watcher.cpp +++ b/src/app/document_files_watcher.cpp @@ -7,9 +7,14 @@ #include "document_files_watcher.h" #include "../base/application.h" +#include "../base/unit_system.h" #include "../qtcommon/filepath_conv.h" #include +#include +#include + +#include namespace Mayo { @@ -38,6 +43,7 @@ void DocumentFilesWatcher::enable(bool on) } else { this->destroyFileSystemWatcher(); + m_setDocBeingChanged.clear(); m_vecNonAckDocumentChanged.clear(); } } @@ -54,6 +60,16 @@ void DocumentFilesWatcher::acknowledgeDocumentFileChange(const DocumentPtr& doc) m_vecNonAckDocumentChanged.erase(it); } +QuantityTime DocumentFilesWatcher::signalSendDelay() const +{ + return m_signalSendDelay; +} + +void DocumentFilesWatcher::setSignalSendDelay(QuantityTime delay) +{ + m_signalSendDelay = delay; +} + QFileSystemWatcher* DocumentFilesWatcher::fileSystemWatcher() { if (!m_fileSystemWatcher) { @@ -78,10 +94,28 @@ void DocumentFilesWatcher::onFileChanged(const QString& strFilePath) if (m_isEnabled) { const FilePath docFilePath = filepathFrom(strFilePath); DocumentPtr doc = m_app->findDocumentByLocation(docFilePath); - if (this->isDocumentChangeAcknowledged(doc)) { - m_vecNonAckDocumentChanged.push_back(doc); - this->signalDocumentFileChanged.send(doc); + + if (!this->isDocumentChangeAcknowledged(doc)) { + //qDebug() << "onFileChanged()" << strFilePath << " -- file change !ack: signal rejected"; + return; + } + + if (m_setDocBeingChanged.find(doc) != m_setDocBeingChanged.cend()) { + //qDebug() << "onFileChanged()" << strFilePath << " -- being modified: signal rejected"; + return; } + + //qDebug() << "onFileChanged()" << strFilePath << " -- register callback: signal accepted"; + m_setDocBeingChanged.insert(doc); + const int sendSignalDelay_ms = UnitSystem::milliseconds(m_signalSendDelay).value; + QTimer::singleShot(sendSignalDelay_ms, this, [=]{ + if (m_setDocBeingChanged.find(doc) != m_setDocBeingChanged.cend()) { + //qDebug() << "onFileChanged()" << strFilePath << " -- timer callback: signal"; + m_vecNonAckDocumentChanged.push_back(doc); + this->signalDocumentFileChanged.send(doc); + m_setDocBeingChanged.erase(doc); + } + }); } } @@ -109,9 +143,10 @@ void DocumentFilesWatcher::onDocumentAdded(const DocumentPtr& doc) void DocumentFilesWatcher::onDocumentAboutToClose(const DocumentPtr& doc) { - if (m_isEnabled) + if (m_isEnabled) { this->fileSystemWatcher()->removePath(filepathTo(doc->filePath())); + m_setDocBeingChanged.erase(doc); + } } - } // namespace Mayo diff --git a/src/app/document_files_watcher.h b/src/app/document_files_watcher.h index ea6bd4df..342b51e6 100644 --- a/src/app/document_files_watcher.h +++ b/src/app/document_files_watcher.h @@ -7,10 +7,12 @@ #include "../base/application_ptr.h" #include "../base/document_ptr.h" #include "../base/filepath.h" +#include "../base/quantity.h" #include "../base/signal.h" #include +#include #include class QFileSystemWatcher; @@ -33,6 +35,9 @@ class DocumentFilesWatcher : public QObject { void acknowledgeDocumentFileChange(const DocumentPtr& doc); + QuantityTime signalSendDelay() const; + void setSignalSendDelay(QuantityTime delay); + private: QFileSystemWatcher* fileSystemWatcher(); void destroyFileSystemWatcher(); @@ -47,7 +52,9 @@ class DocumentFilesWatcher : public QObject { ApplicationPtr m_app; QFileSystemWatcher* m_fileSystemWatcher = nullptr; bool m_isEnabled = false; + std::unordered_set m_setDocBeingChanged; std::vector m_vecNonAckDocumentChanged; + QuantityTime m_signalSendDelay = 1 * Quantity_Second; }; } // namespace Mayo diff --git a/tests/test_app.cpp b/tests/test_app.cpp index 0db98011..7cce3d15 100644 --- a/tests/test_app.cpp +++ b/tests/test_app.cpp @@ -69,10 +69,14 @@ void TestApp::DocumentFilesWatcher_test() auto app = makeOccHandle(); DocumentFilesWatcher docFilesWatcher(app); + const int signalSendDelay_ms = 250; + docFilesWatcher.setSignalSendDelay(signalSendDelay_ms * Quantity_Millisecond); docFilesWatcher.enable(true); Document::Identifier changedDocId = -1; + int signalCallCount = 0; docFilesWatcher.signalDocumentFileChanged.connectSlot([&](DocumentPtr changedDoc) { changedDocId = changedDoc->identifier(); + ++signalCallCount; }); const FilePath cadFilePath = "tests/outputs/temp-cube.ply"; @@ -92,20 +96,25 @@ void TestApp::DocumentFilesWatcher_test() fnCopyCadFile(); const bool okWait = QTest::qWaitFor([&]{ return changedDocId != -1; }); QVERIFY(okWait); + QCOMPARE(signalCallCount, 1); QCOMPARE(changedDocId, doc->identifier()); // Check further file changes are not monitored until last one is acknowledged changedDocId = -1; + signalCallCount = 0; fnCopyCadFile(); - QTest::qWait(125/*ms*/); + QTest::qWait(signalSendDelay_ms * 1.5); QCOMPARE(changedDocId, -1); + QCOMPARE(signalCallCount, 0); // Check closing document unmonitors file in DocumentFilesWatcher docFilesWatcher.acknowledgeDocumentFileChange(doc); app->closeDocument(doc); + changedDocId = -1; fnCopyCadFile(); - QTest::qWait(125/*ms*/); + QTest::qWait(signalSendDelay_ms * 1.5); QCOMPARE(changedDocId, -1); + QCOMPARE(signalCallCount, 0); } void TestApp::FilePathConv_test()