commit cf3c035266486b797e4bc3fe1a9e95c87f45f66d
Author: Guillaume Munch <[email protected]>
Date:   Sun Mar 5 20:12:07 2017 +0100

    Implement real-time detection of external modifications
---
 src/Buffer.cpp                |   69 ++++++++++++++++++++++++++++++++++++----
 src/Buffer.h                  |   29 +++++------------
 src/frontends/qt4/GuiView.cpp |    7 +---
 3 files changed, 73 insertions(+), 32 deletions(-)

diff --git a/src/Buffer.cpp b/src/Buffer.cpp
index 6b41213..2bc454b 100644
--- a/src/Buffer.cpp
+++ b/src/Buffer.cpp
@@ -91,6 +91,7 @@
 #include "support/debug.h"
 #include "support/docstring_list.h"
 #include "support/ExceptionMessage.h"
+#include "support/FileMonitor.h"
 #include "support/FileName.h"
 #include "support/FileNameList.h"
 #include "support/filetools.h"
@@ -376,6 +377,18 @@ public:
        // display the review toolbar, for instance)
        mutable bool tracked_changes_present_;
 
+       // Make sure the file monitor monitors the good file.
+       void refreshFileMonitor();
+
+       /// has it been notified of an external modification?
+       bool isExternallyModified() const { return externally_modified_; }
+
+       /// Notify or clear of external modification
+       void fileExternallyModified(bool modified) const;
+
+       /// Block notifications of external modifications
+       FileMonitorBlocker blockFileMonitor() { return 
file_monitor_->block(10); }
+
 private:
        /// So we can force access via the accessors.
        mutable Buffer const * parent_buffer;
@@ -384,6 +397,10 @@ private:
        int char_count_;
        int blank_count_;
 
+       /// has been externally modified? Can be reset by the user.
+       mutable bool externally_modified_;
+
+       FileMonitorPtr file_monitor_;
 };
 
 
@@ -425,8 +442,10 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, 
bool readonly_,
          inset(0), preview_loader_(0), cloned_buffer_(cloned_buffer),
          clone_list_(0), doing_export(false),
          tracked_changes_present_(0), parent_buffer(0),
-         word_count_(0), char_count_(0), blank_count_(0)
+         word_count_(0), char_count_(0), blank_count_(0),
+         externally_modified_(false)
 {
+       refreshFileMonitor();
        if (!cloned_buffer_) {
                temppath = createBufferTmpDir();
                lyxvc.setBuffer(owner_);
@@ -864,6 +883,7 @@ void Buffer::setFileName(FileName const & fname)
 {
        bool const changed = fname != d->filename;
        d->filename = fname;
+       d->refreshFileMonitor();
        if (changed)
                lyxvc().file_found_hook(fname);
        setReadonly(d->filename.isReadOnly());
@@ -1360,6 +1380,7 @@ FileName Buffer::getBackupName() const {
 // Should probably be moved to somewhere else: BufferView? GuiView?
 bool Buffer::save() const
 {
+       FileMonitorBlocker block = d->blockFileMonitor();
        docstring const file = makeDisplayPath(absFileName(), 20);
        d->filename.refresh();
 
@@ -1375,7 +1396,7 @@ bool Buffer::save() const
        }
 
        // ask if the disk file has been externally modified (use checksum 
method)
-       if (fileName().exists() && isExternallyModified(checksum_method)) {
+       if (fileName().exists() && isChecksumModified()) {
                docstring text =
                        bformat(_("Document %1$s has been externally modified. "
                                "Are you sure you want to overwrite this 
file?"), file);
@@ -2995,13 +3016,10 @@ bool Buffer::isClean() const
 }
 
 
-bool Buffer::isExternallyModified(CheckMethod method) const
+bool Buffer::isChecksumModified() const
 {
        LASSERT(d->filename.exists(), return false);
-       // if method == timestamp, check timestamp before checksum
-       return (method == checksum_method
-               || d->timestamp_ != d->filename.lastModified())
-               && d->checksum_ != d->filename.checksum();
+       return d->checksum_ != d->filename.checksum();
 }
 
 
@@ -3031,6 +3049,7 @@ void Buffer::markClean() const
        // autosave
        d->bak_clean = true;
        d->undo_.markDirty();
+       clearExternalModification();
 }
 
 
@@ -5298,5 +5317,41 @@ void Buffer::updateChangesPresent() const
 }
 
 
+void Buffer::Impl::refreshFileMonitor()
+{
+       if (file_monitor_ && file_monitor_->filename() == 
filename.absFileName())
+               return file_monitor_->refresh();
+
+       // The previous file monitor is invalid
+       // This also destroys the previous file monitor and all its connections
+       file_monitor_ = FileSystemWatcher::monitor(filename);
+       fileExternallyModified(false);
+       // file_monitor_ will be destroyed with *this, so it is not going to 
call a
+       // destroyed object method.
+       file_monitor_->connect([this](){ fileExternallyModified(true); });
+}
+
+
+void Buffer::Impl::fileExternallyModified(bool modified) const
+{
+       if (modified)
+               lyx_clean = bak_clean = false;
+       externally_modified_ = modified;
+       if (wa_)
+               wa_->updateTitles();
+}
+
+
+bool Buffer::notifiesExternalModification() const
+{
+       return d->isExternallyModified();
+}
+
+
+void Buffer::clearExternalModification() const
+{
+       d->fileExternallyModified(false);
+}
+
 
 } // namespace lyx
diff --git a/src/Buffer.h b/src/Buffer.h
index 0453964..957d54c 100644
--- a/src/Buffer.h
+++ b/src/Buffer.h
@@ -144,24 +144,6 @@ public:
                PreviewError
        };
 
-       /// Method to check if a file is externally modified, used by
-       /// isExternallyModified()
-       /**
-        * timestamp is fast but inaccurate. For example, the granularity
-        * of timestamp on a FAT filesystem is 2 seconds. Also, various 
operations
-        * may touch the timestamp of a file even when its content is unchanged.
-        *
-        * checksum is accurate but slow, which can be a problem when it is
-        * frequently used, or used for a large file on a slow (network) file
-        * system.
-        *
-        * FIXME: replace this method with support/FileMonitor.
-        */
-       enum CheckMethod {
-               checksum_method, ///< Use file checksum
-               timestamp_method ///< Use timestamp, and checksum if timestamp 
has changed
-       };
-
        ///
        enum UpdateScope {
                UpdateMaster,
@@ -371,8 +353,15 @@ public:
        ///
        bool isDepClean(std::string const & name) const;
 
-       /// whether or not disk file has been externally modified
-       bool isExternallyModified(CheckMethod method) const;
+       /// Whether or not disk file has been externally modified. Uses a 
checksum
+       /// which is accurate but slow, which can be a problem when it is 
frequently
+       /// used, or used for a large file on a slow (network) file system.
+       bool isChecksumModified() const;
+
+       /// Flag set by the FileSystemWatcher.
+       /// Fast but (not so) inaccurate, can be cleared by the user.
+       bool notifiesExternalModification() const;
+       void clearExternalModification() const;
 
        /// mark the main lyx file as not needing saving
        void markClean() const;
diff --git a/src/frontends/qt4/GuiView.cpp b/src/frontends/qt4/GuiView.cpp
index b4d0810..c595632 100644
--- a/src/frontends/qt4/GuiView.cpp
+++ b/src/frontends/qt4/GuiView.cpp
@@ -1812,9 +1812,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, 
FuncStatus & flag)
 
        case LFUN_BUFFER_RELOAD:
                enable = doc_buffer && !doc_buffer->isUnnamed()
-                       && doc_buffer->fileName().exists()
-                       && (!doc_buffer->isClean()
-                          || 
doc_buffer->isExternallyModified(Buffer::timestamp_method));
+                       && doc_buffer->fileName().exists() && 
!doc_buffer->isClean();
                break;
 
        case LFUN_BUFFER_CHILD_OPEN:
@@ -3070,8 +3068,7 @@ void GuiView::checkExternallyModifiedBuffers()
        BufferList::iterator const bend = theBufferList().end();
        for (; bit != bend; ++bit) {
                Buffer * buf = *bit;
-               if (buf->fileName().exists()
-                       && buf->isExternallyModified(Buffer::checksum_method)) {
+               if (buf->fileName().exists() && buf->isChecksumModified()) {
                        docstring text = bformat(_("Document \n%1$s\n has been 
externally modified."
                                        " Reload now? Any local changes will be 
lost."),
                                        from_utf8(buf->absFileName()));

Reply via email to