4 * This file is part of LyX, the document processor.
5 * Licence details can be found in the file COPYING.
7 * \author Angus Leeming
8 * \author Guillaume Munch
10 * Full author contact details are available in file CREDITS.
12 * FileMonitor monitors a file and informs a listener when that file has
19 #include "support/FileName.h"
20 #include "support/signals.h"
24 #include <QFileSystemWatcher>
33 /// FileMonitor, a file monitor based on QFileSystemWatcher
37 class ActiveFileMonitor;
38 class FileMonitorGuard;
39 typedef std::unique_ptr<FileMonitor> FileMonitorPtr;
40 typedef std::unique_ptr<ActiveFileMonitor> ActiveFileMonitorPtr;
44 /// FileMonitorPtr monitor = FileSystemWatcher::monitor(file_with_path);
45 /// monitor.connect(...); //(using boost::signals2), or:
46 /// connect(monitor, SIGNAL(fileChanged()),...); // (using Qt)
48 /// Remember that a unique_ptr is automatically deleted at the end of a scope if
49 /// it has not been moved, or when assigned. When that happens, the signal
50 /// object is deleted and therefore all the connections are closed. The file
51 /// ceases being tracked when all the monitors for a file have been deleted.
54 /// * as determined statically by the scope, or
55 /// * dynamically, using:
56 /// monitor = nullptr;
58 /// Watch a different file:
59 /// monitor = FileSystemWatcher::monitor(file_with_path2);
60 /// monitor.connect(...);
61 /// (stops watching the first)
63 /// Block notifications for the duration of a scope:
65 /// FileMonitorBlocker block = monitor.block();
69 /// Reset connections:
70 /// monitor.disconnect();
71 /// or the disconnect method of the connection object for the boost signal.
73 class FileSystemWatcher
76 /// as described above
77 static FileMonitorPtr monitor(FileName const & filename);
78 /// same but with an ActiveFileMonitor
79 static ActiveFileMonitorPtr activeMonitor(FileName const & filename,
80 int interval = 10000);
81 /// Output whether the paths tracked by qwatcher_ and the active
82 /// FileMonitorGuards are in correspondence.
86 /// A global instance is created automatically on first call
87 static FileSystemWatcher & instance();
89 std::shared_ptr<FileMonitorGuard> getGuard(FileName const & filename);
90 /// Caches the monitor guards but allow them to be destroyed
91 std::map<std::string, std::weak_ptr<FileMonitorGuard>> store_;
92 /// This class is a wrapper for QFileSystemWatcher
93 std::unique_ptr<QFileSystemWatcher> const qwatcher_;
97 /// Must be unique per path
98 /// Ends the watch when deleted
99 class FileMonitorGuard : public QObject
105 FileMonitorGuard(std::string const & filename,
106 QFileSystemWatcher * qwatcher);
109 /// absolute path being tracked
110 std::string const & filename() { return filename_; }
111 /// if false, emit fileChanged() when we notice the existence of the file
112 void setExists(bool exists) { exists_ = exists; }
115 /// Make sure it is being monitored, after e.g. a deletion. See
116 /// <https://bugreports.qt.io/browse/QTBUG-46483>. This is called
121 /// Connect to this to be notified when the file changes
122 void fileChanged() const;
125 /// Receive notifications from the QFileSystemWatcher
126 void notifyChange(QString const & path);
129 std::string const filename_;
130 QFileSystemWatcher * qwatcher_;
135 class FileMonitorBlockerGuard : public QObject
138 QPointer<FileMonitor> monitor_;
142 FileMonitorBlockerGuard(FileMonitor * monitor);
143 ~FileMonitorBlockerGuard();
144 void setDelay(int delay);
148 typedef std::shared_ptr<FileMonitorBlockerGuard> FileMonitorBlocker;
152 class FileMonitor : public QObject
155 friend class FileMonitorBlockerGuard;
158 FileMonitor(std::shared_ptr<FileMonitorGuard> monitor);
160 typedef signals2::signal<void()> sig;
161 typedef sig::slot_type slot;
162 /// Connect and you'll be informed when the file has changed.
163 signals2::connection connect(slot const &);
164 /// disconnect all slots connected to the boost signal fileChanged_ or to
165 /// the qt signal fileChanged()
167 /// absolute path being tracked
168 std::string const & filename() { return monitor_->filename(); }
169 /// Creates a guard that blocks notifications. Copyable. Notifications from
170 /// this monitor are blocked as long as there are copies of the
171 /// FileMonitorBlocker around.
172 /// \param delay is the amount waited in ms after expiration of the guard
173 /// before reconnecting. It can be used to slow down incoming events
174 /// accordingly. A value of 0 is still made asynchronous, because of the
175 /// fundamentally asynchronous nature of QFileSystemWatcher. To catch one's
176 /// own file operations, a value of 0 for delay is sufficient with the
177 /// inotify backend (e.g. Linux); for OSX (kqueue), a value of 100ms is
178 /// unsufficient and more tests need to be done in combination with
179 /// flushing/syncing to disk in order to understand how to catch one's own
180 /// operations reliably. No feedback from Windows yet. See
181 /// <https://www.mail-archive.com/lyx-devel@lists.lyx.org/msg200252.html>.
182 FileMonitorBlocker block(int delay = 0);
183 /// Make sure the good file is being monitored, after e.g. a move or a
184 /// deletion. See <https://bugreports.qt.io/browse/QTBUG-46483>. This is
185 /// called automatically.
186 void refresh() { monitor_->refresh(); }
189 /// Connect to this to be notified when the file changes
190 void fileChanged() const;
193 /// Receive notifications from the FileMonitorGuard
196 void reconnectToFileMonitorGuard();
201 /// the unique watch for our file
202 std::shared_ptr<FileMonitorGuard> const monitor_;
204 std::weak_ptr<FileMonitorBlockerGuard> blocker_;
208 /// When a more active monitoring style is needed.
209 /// For instance because QFileSystemWatcher does not work for remote file
211 class ActiveFileMonitor : public FileMonitor
215 ActiveFileMonitor(std::shared_ptr<FileMonitorGuard> monitor,
216 FileName const & filename, int interval);
217 /// call checkModified asynchronously
218 void checkModifiedAsync();
221 /// Check explicitly for a modification, but not more than once every
223 void checkModified();
226 void setCooldown() { cooldown_ = true; }
227 void clearCooldown() { cooldown_ = false; }
230 FileName const filename_;
236 unsigned long checksum_;
242 } // namespace support
245 #endif // FILEMONITOR_H