2 * \file FileMonitor.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Guillaume Munch
8 * Full author contact details are available in file CREDITS.
13 #include "support/FileMonitor.h"
15 #include "support/debug.h"
16 #include "support/FileName.h"
17 #include "support/qstring_helpers.h"
18 #include "support/unique_ptr.h"
31 FileSystemWatcher & FileSystemWatcher::instance()
33 // This thread-safe because QFileSystemWatcher is thread-safe.
34 static FileSystemWatcher f;
39 FileSystemWatcher::FileSystemWatcher()
40 : qwatcher_(make_unique<QFileSystemWatcher>())
45 FileMonitorPtr FileSystemWatcher::monitor(FileName const & file_with_path)
47 FileSystemWatcher & f = instance();
48 string const filename = file_with_path.absFileName();
49 weak_ptr<FileMonitorGuard> & wptr = f.store_[filename];
50 if (shared_ptr<FileMonitorGuard> mon = wptr.lock())
51 return make_unique<FileMonitor>(mon);
52 auto mon = make_shared<FileMonitorGuard>(filename, f.qwatcher_.get());
54 return make_unique<FileMonitor>(mon);
59 void FileSystemWatcher::debug()
61 FileSystemWatcher & f = instance();
62 QStringList q_files = f.qwatcher_->files();
63 for (pair<string, weak_ptr<FileMonitorGuard>> pair : f.store_) {
64 string const & name = pair.first;
65 if (!pair.second.expired()) {
66 if (!q_files.contains(toqstr(name)))
67 LYXERR0("Monitored but not QFileSystemWatched (bad): " << name);
69 //LYXERR0("Monitored and QFileSystemWatched (good): " << name);
73 for (QString const & qname : q_files) {
74 string const name = fromqstr(qname);
75 weak_ptr<FileMonitorGuard> & wptr = f.store_[name];
77 LYXERR0("QFileSystemWatched but not monitored (bad): " << name);
82 FileMonitorGuard::FileMonitorGuard(string const & filename,
83 QFileSystemWatcher * qwatcher)
84 : filename_(filename), qwatcher_(qwatcher)
86 QObject::connect(qwatcher, SIGNAL(fileChanged(QString const &)),
87 this, SLOT(notifyChange(QString const &)));
88 if (qwatcher_->files().contains(toqstr(filename)))
89 LYXERR0("This file is already being QFileSystemWatched: " << filename
90 << ". This should not happen.");
95 FileMonitorGuard::~FileMonitorGuard()
97 qwatcher_->removePath(toqstr(filename_));
101 void FileMonitorGuard::refresh(bool new_file)
103 QString const qfilename = toqstr(filename_);
104 if(!qwatcher_->files().contains(qfilename)) {
105 bool exists = QFile(qfilename).exists();
106 if (!exists || !qwatcher_->addPath(qfilename)) {
109 "Could not add path to QFileSystemWatcher: "
111 QTimer::singleShot(1000, this, [=](){
112 refresh(new_file || !exists);
114 } else if (exists && new_file)
115 Q_EMIT fileChanged();
120 void FileMonitorGuard::notifyChange(QString const & path)
122 if (path == toqstr(filename_)) {
123 Q_EMIT fileChanged();
124 // If the file has been modified by delete-move, we are notified of the
125 // deletion but we no longer track the file. See
126 // <https://bugreports.qt.io/browse/QTBUG-46483> (not a bug).
132 FileMonitor::FileMonitor(std::shared_ptr<FileMonitorGuard> monitor)
135 connectToFileMonitorGuard();
140 void FileMonitor::connectToFileMonitorGuard()
142 QObject::connect(monitor_.get(), SIGNAL(fileChanged()),
143 this, SLOT(changed()));
147 boost::signals2::connection
148 FileMonitor::connect(sig::slot_type const & slot)
150 return fileChanged_.connect(slot);
154 void FileMonitor::disconnect()
156 fileChanged_.disconnect_all_slots();
157 QObject::disconnect(this, SIGNAL(fileChanged()));
161 void FileMonitor::changed()
165 Q_EMIT fileChanged();
169 FileMonitorBlocker FileMonitor::block(int delay)
171 FileMonitorBlocker blocker = blocker_.lock();
173 blocker_ = blocker = make_shared<FileMonitorBlockerGuard>(this);
174 blocker->setDelay(delay);
179 FileMonitorBlockerGuard::FileMonitorBlockerGuard(FileMonitor * parent)
180 : QObject(parent), parent_(parent), delay_(0)
182 QObject::disconnect(parent_->monitor_.get(), SIGNAL(fileChanged()),
183 parent_, SLOT(changed()));
187 void FileMonitorBlockerGuard::setDelay(int delay)
189 delay_ = max(delay_, delay);
193 FileMonitorBlockerGuard::~FileMonitorBlockerGuard()
195 // closures can only copy local copies
196 FileMonitor * parent = parent_;
197 // parent is also our QObject::parent() so we are deleted before parent.
198 // Even if delay_ is 0, the QTimer is necessary. Indeed, the notifications
199 // from QFileSystemWatcher that we meant to ignore are not going to be
200 // treated immediately, so we must yield to give us the opportunity to
202 QTimer::singleShot(delay_, parent, [parent]() {
203 parent->connectToFileMonitorGuard();
207 } // namespace support
210 #include "moc_FileMonitor.cpp"