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"
23 #include <QFileSystemWatcher>
27 #include <boost/signals2.hpp>
34 /// FileMonitor, a file monitor based on QFileSystemWatcher
38 class ActiveFileMonitor;
39 class FileMonitorGuard;
40 typedef std::unique_ptr<FileMonitor> FileMonitorPtr;
41 typedef std::unique_ptr<ActiveFileMonitor> ActiveFileMonitorPtr;
45 /// FileMonitorPtr monitor = FileSystemWatcher::monitor(file_with_path);
46 /// monitor.connect(...); //(using boost::signals2), or:
47 /// connect(monitor, SIGNAL(fileChanged()),...); // (using Qt)
49 /// Remember that a unique_ptr is automatically deleted at the end of a scope if
50 /// it has not been moved, or when assigned. When that happens, the signal
51 /// object is deleted and therefore all the connections are closed. The file
52 /// ceases being tracked when all the monitors for a file have been deleted.
55 /// * as determined statically by the scope, or
56 /// * dynamically, using:
57 /// monitor = nullptr;
59 /// Watch a different file:
60 /// monitor = FileSystemWatcher::monitor(file_with_path2);
61 /// monitor.connect(...);
62 /// (stops watching the first)
64 /// Block notifications for the duration of a scope:
66 /// FileMonitorBlocker block = monitor.block();
70 /// Reset connections:
71 /// monitor.disconnect();
72 /// or the disconnect method of the connection object for the boost signal.
74 class FileSystemWatcher
77 /// as described above
78 static FileMonitorPtr monitor(FileName const & filename);
79 /// same but with an ActiveFileMonitor
80 static ActiveFileMonitorPtr activeMonitor(FileName const & filename,
81 int interval = 10000);
82 /// Output whether the paths tracked by qwatcher_ and the active
83 /// FileMonitorGuards are in correspondence.
87 /// A global instance is created automatically on first call
88 static FileSystemWatcher & instance();
90 std::shared_ptr<FileMonitorGuard> getGuard(FileName const & filename);
91 /// Caches the monitor guards but allow them to be destroyed
92 std::map<std::string, std::weak_ptr<FileMonitorGuard>> store_;
93 /// This class is a wrapper for QFileSystemWatcher
94 std::unique_ptr<QFileSystemWatcher> const qwatcher_;
98 /// Must be unique per path
99 /// Ends the watch when deleted
100 class FileMonitorGuard : public QObject
106 FileMonitorGuard(std::string const & filename,
107 QFileSystemWatcher * qwatcher);
110 /// absolute path being tracked
111 std::string const & filename() { return filename_; }
112 /// if false, emit fileChanged() when we notice the existence of the file
113 void setExists(bool exists) { exists_ = exists; }
116 /// Make sure it is being monitored, after e.g. a deletion. See
117 /// <https://bugreports.qt.io/browse/QTBUG-46483>. This is called
122 /// Connect to this to be notified when the file changes
123 void fileChanged() const;
126 /// Receive notifications from the QFileSystemWatcher
127 void notifyChange(QString const & path);
130 std::string const filename_;
131 QFileSystemWatcher * qwatcher_;
136 class FileMonitorBlockerGuard : public QObject
139 QPointer<FileMonitor> monitor_;
143 FileMonitorBlockerGuard(FileMonitor * monitor);
144 ~FileMonitorBlockerGuard();
145 void setDelay(int delay);
149 typedef std::shared_ptr<FileMonitorBlockerGuard> FileMonitorBlocker;
153 class FileMonitor : public QObject
156 friend class FileMonitorBlockerGuard;
159 FileMonitor(std::shared_ptr<FileMonitorGuard> monitor);
161 typedef boost::signals2::signal<void()> sig;
162 /// Connect and you'll be informed when the file has changed.
163 boost::signals2::connection connect(sig::slot_type 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 around.
171 /// \param delay is the amount waited in ms after expiration of the guard
172 /// before reconnecting. This delay thing is to deal with asynchronous
173 /// notifications in a not so elegant fashion. But it can also be used to
174 /// slow down incoming events.
175 FileMonitorBlocker block(int delay = 0);
176 /// Make sure the good file is being monitored, after e.g. a move or a
177 /// deletion. See <https://bugreports.qt.io/browse/QTBUG-46483>. This is
178 /// called automatically.
179 void refresh() { return monitor_->refresh(); }
182 /// Connect to this to be notified when the file changes
183 void fileChanged() const;
186 /// Receive notifications from the FileMonitorGuard
189 void reconnectToFileMonitorGuard();
194 /// the unique watch for our file
195 std::shared_ptr<FileMonitorGuard> const monitor_;
197 std::weak_ptr<FileMonitorBlockerGuard> blocker_;
201 /// When a more active monitoring style is needed.
202 /// For instance because QFileSystemWatcher does not work for remote file
204 class ActiveFileMonitor : public FileMonitor
208 ActiveFileMonitor(std::shared_ptr<FileMonitorGuard> monitor,
209 FileName const & filename, int interval);
210 /// call checkModified asynchronously
211 void checkModifiedAsync();
214 /// Check explicitly for a modification, but not more than once every
216 void checkModified();
219 void setCooldown() { cooldown_ = true; }
220 void clearCooldown() { cooldown_ = false; }
223 FileName const filename_;
229 unsigned long checksum_;
235 } // namespace support
238 #endif // FILEMONITOR_H