From eabb97f5762c65fc3d54ae03d5a57f1821e31a3c 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/document_files_watcher.cpp | 30 +++++++++++++++++------------- src/app/document_files_watcher.h | 13 ++++++------- src/app/widget_main_control.cpp | 5 +---- tests/test_app.cpp | 8 +------- 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/app/document_files_watcher.cpp b/src/app/document_files_watcher.cpp index f57477e6..8f6917a2 100644 --- a/src/app/document_files_watcher.cpp +++ b/src/app/document_files_watcher.cpp @@ -7,10 +7,13 @@ #include "document_files_watcher.h" #include "../base/application.h" +#include "../base/unit_system.h" #include "../qtcommon/filepath_conv.h" #include +#include + namespace Mayo { DocumentFilesWatcher::DocumentFilesWatcher(const ApplicationPtr& app, QObject* parent) @@ -20,6 +23,7 @@ DocumentFilesWatcher::DocumentFilesWatcher(const ApplicationPtr& app, QObject* p app->signalDocumentAdded.connectSlot(&DocumentFilesWatcher::onDocumentAdded, this); app->signalDocumentAboutToClose.connectSlot(&DocumentFilesWatcher::onDocumentAboutToClose, this); app->signalDocumentFilePathChanged.connectSlot(&DocumentFilesWatcher::onDocumentFilePathChanged, this); + m_timer.start(); } void DocumentFilesWatcher::enable(bool on) @@ -38,7 +42,7 @@ void DocumentFilesWatcher::enable(bool on) } else { this->destroyFileSystemWatcher(); - m_vecNonAckDocumentChanged.clear(); + m_mapDocLastChangeTime.clear(); } } @@ -47,11 +51,10 @@ bool DocumentFilesWatcher::isEnabled() const return m_isEnabled; } -void DocumentFilesWatcher::acknowledgeDocumentFileChange(const DocumentPtr& doc) +QuantityTime DocumentFilesWatcher::minimumDelayBetweenTwoDistinctFileChanges() { - auto it = std::find(m_vecNonAckDocumentChanged.begin(), m_vecNonAckDocumentChanged.end(), doc); - if (it != m_vecNonAckDocumentChanged.end()) - m_vecNonAckDocumentChanged.erase(it); + constexpr QuantityTime minDelay = 50 * Quantity_Millisecond; + return minDelay; } QFileSystemWatcher* DocumentFilesWatcher::fileSystemWatcher() @@ -78,19 +81,20 @@ 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); + auto itLastChangeTime = m_mapDocLastChangeTime.find(doc); + int64_t timeDiff = -1; + if (itLastChangeTime != m_mapDocLastChangeTime.cend()) + timeDiff = m_timer.elapsed() - itLastChangeTime->second; + + const double minDelay = UnitSystem::milliseconds(minimumDelayBetweenTwoDistinctFileChanges()); + //qDebug() << "onFileChanged()" << " timeDiff=" << timeDiff << " minDelay=" << minDelay; + if (timeDiff < 0 || timeDiff > minDelay) { + m_mapDocLastChangeTime.insert_or_assign(doc, m_timer.elapsed()); this->signalDocumentFileChanged.send(doc); } } } -bool DocumentFilesWatcher::isDocumentChangeAcknowledged(const DocumentPtr& doc) const -{ - auto it = std::find(m_vecNonAckDocumentChanged.cbegin(), m_vecNonAckDocumentChanged.cend(), doc); - return it == m_vecNonAckDocumentChanged.cend(); -} - void DocumentFilesWatcher::onDocumentFilePathChanged(const DocumentPtr&, const FilePath& fp) { if (m_isEnabled) { diff --git a/src/app/document_files_watcher.h b/src/app/document_files_watcher.h index ea6bd4df..8a5e5902 100644 --- a/src/app/document_files_watcher.h +++ b/src/app/document_files_watcher.h @@ -7,11 +7,13 @@ #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 +#include class QFileSystemWatcher; @@ -20,8 +22,6 @@ namespace Mayo { // Monitors the file system for changes to Document files owned by Application object // When a Document is opened then DocumentFilesWatcher automatically monitors the corresponding file // for changes. When an Document is closed then monitoring for that file is terminated -// File changes need to be acknowledged with DocumentFilesWatcher:acknowledgeDocumentFileChange() -// otherwise there won't be further signal notification for the changed file class DocumentFilesWatcher : public QObject { public: DocumentFilesWatcher(const ApplicationPtr& app, QObject* parent = nullptr); @@ -31,15 +31,13 @@ class DocumentFilesWatcher : public QObject { void enable(bool on); bool isEnabled() const; - void acknowledgeDocumentFileChange(const DocumentPtr& doc); + static QuantityTime minimumDelayBetweenTwoDistinctFileChanges(); private: QFileSystemWatcher* fileSystemWatcher(); void destroyFileSystemWatcher(); void onFileChanged(const QString& strFilePath); - bool isDocumentChangeAcknowledged(const DocumentPtr& doc) const; - void onDocumentFilePathChanged(const DocumentPtr& doc, const FilePath& fp); void onDocumentAdded(const DocumentPtr& doc); void onDocumentAboutToClose(const DocumentPtr& doc); @@ -47,7 +45,8 @@ class DocumentFilesWatcher : public QObject { ApplicationPtr m_app; QFileSystemWatcher* m_fileSystemWatcher = nullptr; bool m_isEnabled = false; - std::vector m_vecNonAckDocumentChanged; + std::unordered_map m_mapDocLastChangeTime; + QElapsedTimer m_timer; }; } // namespace Mayo diff --git a/src/app/widget_main_control.cpp b/src/app/widget_main_control.cpp index ebf14519..2deb6726 100644 --- a/src/app/widget_main_control.cpp +++ b/src/app/widget_main_control.cpp @@ -340,10 +340,8 @@ void WidgetMainControl::reloadDocumentAfterChange(const DocumentPtr& doc) const auto& propActionOnDocumentFileChange = AppModule::get()->properties()->actionOnDocumentFileChange; // Option: silent reloading - if (propActionOnDocumentFileChange == ActionOnDocumentFileChange::ReloadSilently) { - m_docFilesWatcher->acknowledgeDocumentFileChange(doc); + if (propActionOnDocumentFileChange == ActionOnDocumentFileChange::ReloadSilently) fnReloadDoc(doc); - } // Option: ask user to confirm reloading if (propActionOnDocumentFileChange == ActionOnDocumentFileChange::ReloadIfUserConfirm) { @@ -359,7 +357,6 @@ void WidgetMainControl::reloadDocumentAfterChange(const DocumentPtr& doc) msgBox->setTextFormat(Qt::MarkdownText); QtWidgetsUtils::asyncDialogExec(msgBox); QObject::connect(msgBox, &QMessageBox::buttonClicked, this, [=](QAbstractButton* btn) { - m_docFilesWatcher->acknowledgeDocumentFileChange(doc); if (btn == msgBox->button(QMessageBox::Yes)) fnReloadDoc(doc); }); diff --git a/tests/test_app.cpp b/tests/test_app.cpp index 0db98011..35512b8e 100644 --- a/tests/test_app.cpp +++ b/tests/test_app.cpp @@ -94,15 +94,9 @@ void TestApp::DocumentFilesWatcher_test() QVERIFY(okWait); QCOMPARE(changedDocId, doc->identifier()); - // Check further file changes are not monitored until last one is acknowledged - changedDocId = -1; - fnCopyCadFile(); - QTest::qWait(125/*ms*/); - QCOMPARE(changedDocId, -1); - // Check closing document unmonitors file in DocumentFilesWatcher - docFilesWatcher.acknowledgeDocumentFileChange(doc); app->closeDocument(doc); + changedDocId = -1; fnCopyCadFile(); QTest::qWait(125/*ms*/); QCOMPARE(changedDocId, -1);