If the external modification is a deletion, do not ask for reloading.

The behavour when hitting "reload" on a deleted file was also confusing.
This commit is contained in:
Guillaume MM 2017-06-05 22:04:07 +02:00
parent 2058faaa3b
commit b30161b591
5 changed files with 75 additions and 67 deletions

View File

@ -379,15 +379,15 @@ public:
// 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;
void fileExternallyModified(bool exists) const;
/// Block notifications of external modifications
FileMonitorBlocker blockFileMonitor() { return file_monitor_->block(); }
/// has been externally modified? Can be reset by the user.
mutable bool externally_modified_;
private:
/// So we can force access via the accessors.
mutable Buffer const * parent_buffer;
@ -396,9 +396,6 @@ private:
int char_count_;
int blank_count_;
/// has been externally modified? Can be reset by the user.
mutable bool externally_modified_;
FileMonitorPtr file_monitor_;
};
@ -440,9 +437,8 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_,
bibfile_cache_valid_(false), cite_labels_valid_(false), preview_error_(false),
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),
externally_modified_(false)
tracked_changes_present_(0), externally_modified_(false), parent_buffer(0),
word_count_(0), char_count_(0), blank_count_(0)
{
refreshFileMonitor();
if (!cloned_buffer_) {
@ -5332,26 +5328,29 @@ void Buffer::Impl::refreshFileMonitor()
// 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); });
file_monitor_->connect([this](bool exists) {
fileExternallyModified(exists);
});
}
void Buffer::Impl::fileExternallyModified(bool modified) const
void Buffer::Impl::fileExternallyModified(bool const exists) const
{
if (modified) {
// prevent false positives, because FileMonitorBlocker is not enough on
// OSX.
if (filename.exists() && checksum_ == filename.checksum()) {
if (checksum_ == filename.checksum()) {
LYXERR(Debug::FILES, "External modification but "
"checksum unchanged: " << filename);
return;
}
lyx_clean = bak_clean = false;
}
externally_modified_ = modified;
// If the file has been deleted, only mark the file as dirty since it is
// pointless to prompt for reloading. If later a file is moved into this
// location, then the externally modified warning will appear then.
if (exists)
externally_modified_ = true;
if (wa_)
wa_->updateTitles();
}
@ -5359,13 +5358,15 @@ void Buffer::Impl::fileExternallyModified(bool modified) const
bool Buffer::notifiesExternalModification() const
{
return d->isExternallyModified();
return d->externally_modified_;
}
void Buffer::clearExternalModification() const
{
d->fileExternallyModified(false);
d->externally_modified_ = false;
if (d->wa_)
d->wa_->updateTitles();
}

View File

@ -220,7 +220,7 @@ void CacheItem::Impl::startMonitor()
return;
monitor_ = FileSystemWatcher::activeMonitor(filename_);
// Disconnected at the same time as this is destroyed.
monitor_->connect([=](){ startLoading(); });
monitor_->connect([=](bool /* exists */){ startLoading(); });
}

View File

@ -304,7 +304,7 @@ void RenderMonitoredPreview::startMonitoring() const
{
if (!monitoring()) {
monitor_ = FileSystemWatcher::activeMonitor(filename_);
monitor_->connect(changed_);
monitor_->connect([this](bool /* exists */){ changed_(); });
}
}

View File

@ -118,31 +118,37 @@ FileMonitorGuard::~FileMonitorGuard()
}
void FileMonitorGuard::refresh()
void FileMonitorGuard::refresh(bool const emit)
{
if (filename_.empty())
return;
QString const qfilename = toqstr(filename_);
if(!qwatcher_->files().contains(qfilename)) {
bool exists = QFile(qfilename).exists();
if (!qwatcher_->files().contains(qfilename)) {
bool const existed = exists_;
exists_ = QFile(qfilename).exists();
#if (QT_VERSION >= 0x050000)
if (!exists || !qwatcher_->addPath(qfilename))
if (exists_ && !qwatcher_->addPath(qfilename))
#else
auto add_path = [&]() {
qwatcher_->addPath(qfilename);
return qwatcher_->files().contains(qfilename);
};
if (!exists || !add_path())
if (exists_ && !add_path())
#endif
{
if (exists)
LYXERR(Debug::FILES,
"Could not add path to QFileSystemWatcher: "
<< filename_);
QTimer::singleShot(2000, this, SLOT(refresh()));
} else if (exists && !exists_)
Q_EMIT fileChanged();
setExists(exists);
"Could not add path to QFileSystemWatcher: " << filename_);
QTimer::singleShot(5000, this, SLOT(refresh()));
} else {
if (!exists_)
// The standard way to overwrite a file is to delete it and
// create a new file with the same name. Therefore if the file
// has just been deleted, it is smart to check not too long
// after whether it has been recreated.
QTimer::singleShot(existed ? 100 : 2000, this, SLOT(refresh()));
if (existed != exists_ && emit)
Q_EMIT fileChanged(exists_);
}
}
}
@ -150,11 +156,12 @@ void FileMonitorGuard::refresh()
void FileMonitorGuard::notifyChange(QString const & path)
{
if (path == toqstr(filename_)) {
Q_EMIT fileChanged();
// If the file has been modified by delete-move, we are notified of the
// deletion but we no longer track the file. See
// <https://bugreports.qt.io/browse/QTBUG-46483> (not a bug).
refresh();
// Therefore we must refresh.
refresh(false);
Q_EMIT fileChanged(exists_);
}
}
@ -162,17 +169,15 @@ void FileMonitorGuard::notifyChange(QString const & path)
FileMonitor::FileMonitor(std::shared_ptr<FileMonitorGuard> monitor)
: monitor_(monitor)
{
QObject::connect(monitor_.get(), SIGNAL(fileChanged()),
this, SLOT(changed()));
connectToFileMonitorGuard();
refresh();
}
void FileMonitor::reconnectToFileMonitorGuard()
void FileMonitor::connectToFileMonitorGuard()
{
monitor_->setExists(true);
QObject::connect(monitor_.get(), SIGNAL(fileChanged()),
this, SLOT(changed()));
QObject::connect(monitor_.get(), SIGNAL(fileChanged(bool)),
this, SLOT(changed(bool)));
}
@ -185,15 +190,15 @@ signals2::connection FileMonitor::connect(slot const & slot)
void FileMonitor::disconnect()
{
fileChanged_.disconnect_all_slots();
QObject::disconnect(this, SIGNAL(fileChanged()));
QObject::disconnect(this, SIGNAL(fileChanged(bool)));
}
void FileMonitor::changed()
void FileMonitor::changed(bool const exists)
{
// emit boost signal
fileChanged_();
Q_EMIT fileChanged();
fileChanged_(exists);
Q_EMIT fileChanged(exists);
}
@ -210,8 +215,8 @@ FileMonitorBlocker FileMonitor::block(int delay)
FileMonitorBlockerGuard::FileMonitorBlockerGuard(FileMonitor * monitor)
: monitor_(monitor), delay_(0)
{
QObject::disconnect(monitor->monitor_.get(), SIGNAL(fileChanged()),
monitor, SLOT(changed()));
QObject::disconnect(monitor->monitor_.get(), SIGNAL(fileChanged(bool)),
monitor, SLOT(changed(bool)));
}
@ -229,7 +234,7 @@ FileMonitorBlockerGuard::~FileMonitorBlockerGuard()
// from QFileSystemWatcher that we meant to ignore are not going to be
// treated immediately, so we must yield to give us the opportunity to
// ignore them.
QTimer::singleShot(delay_, monitor_, SLOT(reconnectToFileMonitorGuard()));
QTimer::singleShot(delay_, monitor_, SLOT(connectToFileMonitorGuard()));
}
@ -238,8 +243,9 @@ ActiveFileMonitor::ActiveFileMonitor(std::shared_ptr<FileMonitorGuard> monitor,
: FileMonitor(monitor), filename_(filename), interval_(interval),
timestamp_(0), checksum_(0), cooldown_(true)
{
QObject::connect(this, SIGNAL(fileChanged()), this, SLOT(setCooldown()));
QObject::connect(this, SIGNAL(fileChanged(bool)), this, SLOT(setCooldown()));
QTimer::singleShot(interval_, this, SLOT(clearCooldown()));
filename_.refresh();
if (!filename_.exists())
return;
timestamp_ = filename_.lastModified();
@ -254,7 +260,9 @@ void ActiveFileMonitor::checkModified()
cooldown_ = true;
bool changed = false;
if (!filename_.exists()) {
filename_.refresh();
bool exists = filename_.exists();
if (!exists) {
changed = timestamp_ || checksum_;
timestamp_ = 0;
checksum_ = 0;
@ -272,7 +280,7 @@ void ActiveFileMonitor::checkModified()
}
}
if (changed)
FileMonitor::changed();
FileMonitor::changed(exists);
QTimer::singleShot(interval_, this, SLOT(clearCooldown()));
}

View File

@ -108,18 +108,16 @@ public:
~FileMonitorGuard();
/// absolute path being tracked
std::string const & filename() { return filename_; }
/// if false, emit fileChanged() when we notice the existence of the file
void setExists(bool exists) { exists_ = exists; }
public Q_SLOTS:
/// Make sure it is being monitored, after e.g. a deletion. See
/// <https://bugreports.qt.io/browse/QTBUG-46483>. This is called
/// automatically.
void refresh();
void refresh(bool emit = true);
Q_SIGNALS:
/// Connect to this to be notified when the file changes
void fileChanged() const;
void fileChanged(bool exists) const;
private Q_SLOTS:
/// Receive notifications from the QFileSystemWatcher
@ -128,6 +126,7 @@ private Q_SLOTS:
private:
std::string const filename_;
QFileSystemWatcher * qwatcher_;
/// for emitting fileChanged() when the file is created or deleted
bool exists_;
};
@ -157,7 +156,7 @@ class FileMonitor : public QObject
public:
FileMonitor(std::shared_ptr<FileMonitorGuard> monitor);
typedef signals2::signal<void()> sig;
typedef signals2::signal<void(bool)> sig;
typedef sig::slot_type slot;
/// Connect and you'll be informed when the file has changed.
signals2::connection connect(slot const &);
@ -177,7 +176,7 @@ public:
/// inotify backend (e.g. Linux); for OSX (kqueue), a value of 100ms is
/// unsufficient and more tests need to be done in combination with
/// flushing/syncing to disk in order to understand how to catch one's own
/// operations reliably. No feedback from Windows yet. See
/// operations reliably. No feedback about Windows. See
/// <https://www.mail-archive.com/lyx-devel@lists.lyx.org/msg200252.html>.
FileMonitorBlocker block(int delay = 0);
/// Make sure the good file is being monitored, after e.g. a move or a
@ -187,13 +186,13 @@ public:
Q_SIGNALS:
/// Connect to this to be notified when the file changes
void fileChanged() const;
void fileChanged(bool exists) const;
protected Q_SLOTS:
/// Receive notifications from the FileMonitorGuard
void changed();
void changed(bool exists);
///
void reconnectToFileMonitorGuard();
void connectToFileMonitorGuard();
private:
/// boost signal