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()));