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"
21 #include <QStringList>
32 FileSystemWatcher & FileSystemWatcher::instance()
34 // This is thread-safe because QFileSystemWatcher is thread-safe.
35 static FileSystemWatcher f;
40 FileSystemWatcher::FileSystemWatcher()
41 : qwatcher_(make_unique<QFileSystemWatcher>())
46 FileMonitorPtr FileSystemWatcher::monitor(FileName const & file_with_path)
48 FileSystemWatcher & f = instance();
49 string const filename = file_with_path.absFileName();
50 weak_ptr<FileMonitorGuard> & wptr = f.store_[filename];
51 if (shared_ptr<FileMonitorGuard> mon = wptr.lock())
52 return make_unique<FileMonitor>(mon);
53 auto mon = make_shared<FileMonitorGuard>(filename, f.qwatcher_.get());
55 return make_unique<FileMonitor>(mon);
60 void FileSystemWatcher::debug()
62 FileSystemWatcher & f = instance();
63 QStringList q_files = f.qwatcher_->files();
64 for (pair<string, weak_ptr<FileMonitorGuard>> pair : f.store_) {
65 string const & name = pair.first;
66 if (!pair.second.expired()) {
67 if (!q_files.contains(toqstr(name)))
68 LYXERR0("Monitored but not QFileSystemWatched (bad): " << name);
70 //LYXERR0("Monitored and QFileSystemWatched (good): " << name);
74 for (QString const & qname : q_files) {
75 string const name = fromqstr(qname);
76 weak_ptr<FileMonitorGuard> & wptr = f.store_[name];
78 LYXERR0("QFileSystemWatched but not monitored (bad): " << name);
83 FileMonitorGuard::FileMonitorGuard(string const & filename,
84 QFileSystemWatcher * qwatcher)
85 : filename_(filename), qwatcher_(qwatcher), exists_(true)
87 QObject::connect(qwatcher, SIGNAL(fileChanged(QString const &)),
88 this, SLOT(notifyChange(QString const &)));
89 if (qwatcher_->files().contains(toqstr(filename)))
90 LYXERR0("This file is already being QFileSystemWatched: " << filename
91 << ". This should not happen.");
96 FileMonitorGuard::~FileMonitorGuard()
98 qwatcher_->removePath(toqstr(filename_));
102 void FileMonitorGuard::refresh()
104 QString const qfilename = toqstr(filename_);
105 if(!qwatcher_->files().contains(qfilename)) {
106 bool exists = QFile(qfilename).exists();
107 #if (QT_VERSION >= 0x050000)
108 if (!exists || !qwatcher_->addPath(qfilename)) {
110 auto add_path = [&]() {
111 qwatcher_->addPath(qfilename);
112 return qwatcher_->files().contains(qfilename);
114 if (!exists || !add_path()) {
118 "Could not add path to QFileSystemWatcher: "
120 QTimer::singleShot(2000, this, SLOT(refresh()));
121 } else if (exists && !exists_)
122 Q_EMIT fileChanged();
128 void FileMonitorGuard::notifyChange(QString const & path)
130 if (path == toqstr(filename_)) {
131 Q_EMIT fileChanged();
132 // If the file has been modified by delete-move, we are notified of the
133 // deletion but we no longer track the file. See
134 // <https://bugreports.qt.io/browse/QTBUG-46483> (not a bug).
140 FileMonitor::FileMonitor(std::shared_ptr<FileMonitorGuard> monitor)
143 QObject::connect(monitor_.get(), SIGNAL(fileChanged()),
144 this, SLOT(changed()));
149 void FileMonitor::reconnectToFileMonitorGuard()
151 monitor_->setExists(true);
152 QObject::connect(monitor_.get(), SIGNAL(fileChanged()),
153 this, SLOT(changed()));
157 boost::signals2::connection
158 FileMonitor::connect(sig::slot_type const & slot)
160 return fileChanged_.connect(slot);
164 void FileMonitor::disconnect()
166 fileChanged_.disconnect_all_slots();
167 QObject::disconnect(this, SIGNAL(fileChanged()));
171 void FileMonitor::changed()
175 Q_EMIT fileChanged();
179 FileMonitorBlocker FileMonitor::block(int delay)
181 FileMonitorBlocker blocker = blocker_.lock();
183 blocker_ = blocker = make_shared<FileMonitorBlockerGuard>(this);
184 blocker->setDelay(delay);
189 FileMonitorBlockerGuard::FileMonitorBlockerGuard(FileMonitor * monitor)
190 : monitor_(monitor), delay_(0)
192 QObject::disconnect(monitor->monitor_.get(), SIGNAL(fileChanged()),
193 monitor, SLOT(changed()));
197 void FileMonitorBlockerGuard::setDelay(int delay)
199 delay_ = max(delay_, delay);
203 FileMonitorBlockerGuard::~FileMonitorBlockerGuard()
207 // Even if delay_ is 0, the QTimer is necessary. Indeed, the notifications
208 // from QFileSystemWatcher that we meant to ignore are not going to be
209 // treated immediately, so we must yield to give us the opportunity to
211 QTimer::singleShot(delay_, monitor_, SLOT(reconnectToFileMonitorGuard()));
214 } // namespace support
217 #include "moc_FileMonitor.cpp"