X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fsupport%2FFileMonitor.cpp;h=c703d2bba7480df28f7dafd3b067fb7e78cdcfe3;hb=28be7d552f62cc02fa86d7f79201d089bfb2d7b5;hp=c14d3d6e3a3e11d8b574f54d6e3d7bf6d0a94cb7;hpb=fa9ab74ffa85a94eb215b5c5e689fac324919d30;p=lyx.git diff --git a/src/support/FileMonitor.cpp b/src/support/FileMonitor.cpp index c14d3d6e3a..c703d2bba7 100644 --- a/src/support/FileMonitor.cpp +++ b/src/support/FileMonitor.cpp @@ -3,6 +3,7 @@ * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * + * \author Angus Leeming * \author Guillaume Munch * * Full author contact details are available in file CREDITS. @@ -42,17 +43,32 @@ FileSystemWatcher::FileSystemWatcher() {} -//static -FileMonitorPtr FileSystemWatcher::monitor(FileName const & file_with_path) +shared_ptr +FileSystemWatcher::getGuard(FileName const & filename) { - FileSystemWatcher & f = instance(); - string const filename = file_with_path.absFileName(); - weak_ptr & wptr = f.store_[filename]; + string const absfilename = filename.absFileName(); + weak_ptr & wptr = store_[absfilename]; if (shared_ptr mon = wptr.lock()) - return make_unique(mon); - auto mon = make_shared(filename, f.qwatcher_.get()); + return mon; + auto mon = make_shared(absfilename, qwatcher_.get()); wptr = mon; - return make_unique(mon); + return mon; +} + + +//static +FileMonitorPtr FileSystemWatcher::monitor(FileName const & filename) +{ + return make_unique(instance().getGuard(filename)); +} + + +//static +ActiveFileMonitorPtr FileSystemWatcher::activeMonitor(FileName const & filename, + int interval) +{ + return make_unique(instance().getGuard(filename), + filename, interval); } @@ -82,8 +98,10 @@ void FileSystemWatcher::debug() FileMonitorGuard::FileMonitorGuard(string const & filename, QFileSystemWatcher * qwatcher) - : filename_(filename), qwatcher_(qwatcher) + : filename_(filename), qwatcher_(qwatcher), exists_(true) { + if (filename.empty()) + return; QObject::connect(qwatcher, SIGNAL(fileChanged(QString const &)), this, SLOT(notifyChange(QString const &))); if (qwatcher_->files().contains(toqstr(filename))) @@ -95,38 +113,42 @@ FileMonitorGuard::FileMonitorGuard(string const & filename, FileMonitorGuard::~FileMonitorGuard() { - qwatcher_->removePath(toqstr(filename_)); + if (!filename_.empty()) + qwatcher_->removePath(toqstr(filename_)); } -void FileMonitorGuard::refresh(bool new_file) +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_); - if (new_file || !exists) - QTimer::singleShot(1000, this, SLOT(refreshTrue())); - else - QTimer::singleShot(1000, this, SLOT(refreshFalse())); - // Better (qt>=5.4): - /*QTimer::singleShot(1000, this, [=](){ - refresh(new_file || !exists); - });*/ - } else if (exists && new_file) - Q_EMIT fileChanged(); + { + LYXERR(Debug::FILES, + "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_); + } } } @@ -134,11 +156,12 @@ void FileMonitorGuard::refresh(bool new_file) 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 // (not a bug). - refresh(); + // Therefore we must refresh. + refresh(false); + Q_EMIT fileChanged(exists_); } } @@ -153,13 +176,12 @@ FileMonitor::FileMonitor(std::shared_ptr monitor) void FileMonitor::connectToFileMonitorGuard() { - QObject::connect(monitor_.get(), SIGNAL(fileChanged()), - this, SLOT(changed())); + QObject::connect(monitor_.get(), SIGNAL(fileChanged(bool)), + this, SLOT(changed(bool))); } -boost::signals2::connection -FileMonitor::connect(sig::slot_type const & slot) +signals2::connection FileMonitor::connect(slot const & slot) { return fileChanged_.connect(slot); } @@ -168,52 +190,72 @@ FileMonitor::connect(sig::slot_type 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); } -FileMonitorBlocker FileMonitor::block(int delay) +ActiveFileMonitor::ActiveFileMonitor(std::shared_ptr monitor, + FileName const & filename, int interval) + : FileMonitor(monitor), filename_(filename), interval_(interval), + timestamp_(0), checksum_(0), cooldown_(true) { - FileMonitorBlocker blocker = blocker_.lock(); - if (!blocker) - blocker_ = blocker = make_shared(this); - blocker->setDelay(delay); - return blocker; + QObject::connect(this, SIGNAL(fileChanged(bool)), this, SLOT(setCooldown())); + QTimer::singleShot(interval_, this, SLOT(clearCooldown())); + filename_.refresh(); + if (!filename_.exists()) + return; + timestamp_ = filename_.lastModified(); + checksum_ = filename_.checksum(); } -FileMonitorBlockerGuard::FileMonitorBlockerGuard(FileMonitor * monitor) - : monitor_(monitor), delay_(0) +void ActiveFileMonitor::checkModified() { - QObject::disconnect(monitor->monitor_.get(), SIGNAL(fileChanged()), - monitor, SLOT(changed())); + if (cooldown_) + return; + + cooldown_ = true; + bool changed = false; + filename_.refresh(); + bool exists = filename_.exists(); + if (!exists) { + changed = timestamp_ || checksum_; + timestamp_ = 0; + checksum_ = 0; + } else { + time_t const new_timestamp = filename_.lastModified(); + + if (new_timestamp != timestamp_) { + timestamp_ = new_timestamp; + + unsigned long const new_checksum = filename_.checksum(); + if (new_checksum != checksum_) { + checksum_ = new_checksum; + changed = true; + } + } + } + if (changed) + FileMonitor::changed(exists); + QTimer::singleShot(interval_, this, SLOT(clearCooldown())); } -void FileMonitorBlockerGuard::setDelay(int delay) +void ActiveFileMonitor::checkModifiedAsync() { - delay_ = max(delay_, delay); + if (!cooldown_) + QTimer::singleShot(0, this, SLOT(checkModified())); } -FileMonitorBlockerGuard::~FileMonitorBlockerGuard() -{ - if (!monitor_) - return; - // Even if delay_ is 0, the QTimer is necessary. Indeed, the notifications - // 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())); -} } // namespace support } // namespace lyx