]> git.lyx.org Git - lyx.git/blob - src/support/FileMonitor.h
Check return value of regex_match instead of looking at first match
[lyx.git] / src / support / FileMonitor.h
1 // -*- C++ -*-
2 /**
3  * \file FileMonitor.h
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Angus Leeming
8  * \author Guillaume Munch
9  *
10  * Full author contact details are available in file CREDITS.
11  *
12  * FileMonitor monitors a file and informs a listener when that file has
13  * changed.
14  */
15
16 #ifndef FILEMONITOR_H
17 #define FILEMONITOR_H
18
19 #include "support/FileName.h"
20
21 #include <memory>
22
23 #include <QFileSystemWatcher>
24 #include <QObject>
25 #include <QPointer>
26
27 #include <boost/signals2.hpp>
28
29
30 namespace lyx {
31 namespace support {
32
33 ///
34 ///  FileMonitor, a file monitor based on QFileSystemWatcher
35 ///
36
37 class FileMonitor;
38 class ActiveFileMonitor;
39 class FileMonitorGuard;
40 typedef std::unique_ptr<FileMonitor> FileMonitorPtr;
41 typedef std::unique_ptr<ActiveFileMonitor> ActiveFileMonitorPtr;
42
43 ///
44 /// Watch a file:
45 ///   FileMonitorPtr monitor = FileSystemWatcher::monitor(file_with_path);
46 ///   monitor.connect(...); //(using boost::signals2), or:
47 ///   connect(monitor, SIGNAL(fileChanged()),...); // (using Qt)
48 ///
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.
53 ///
54 /// Stop watching:
55 ///   * as determined statically by the scope, or
56 ///   * dynamically, using:
57 ///       monitor = nullptr;
58 ///
59 /// Watch a different file:
60 ///   monitor = FileSystemWatcher::monitor(file_with_path2);
61 ///   monitor.connect(...);
62 /// (stops watching the first)
63 ///
64 /// Block notifications for the duration of a scope:
65 ///   {
66 ///       FileMonitorBlocker block = monitor.block();
67 ///       ...
68 ///   }
69 ///
70 /// Reset connections:
71 ///   monitor.disconnect();
72 ///   or the disconnect method of the connection object for the boost signal.
73 ///
74 class FileSystemWatcher
75 {
76 public:
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.
84         static void debug();
85 private:
86         FileSystemWatcher();
87         /// A global instance is created automatically on first call
88         static FileSystemWatcher & instance();
89         ///
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_;
95 };
96
97
98 /// Must be unique per path
99 /// Ends the watch when deleted
100 class FileMonitorGuard : public QObject
101 {
102         Q_OBJECT
103
104 public:
105         /// Start the watch
106         FileMonitorGuard(std::string const & filename,
107                          QFileSystemWatcher * qwatcher);
108         /// End the watch
109         ~FileMonitorGuard();
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; }
114
115 public Q_SLOTS:
116         /// Make sure it is being monitored, after e.g. a deletion. See
117         /// <https://bugreports.qt.io/browse/QTBUG-46483>. This is called
118         /// automatically.
119         void refresh();
120
121 Q_SIGNALS:
122         /// Connect to this to be notified when the file changes
123         void fileChanged() const;
124
125 private Q_SLOTS:
126         /// Receive notifications from the QFileSystemWatcher
127         void notifyChange(QString const & path);
128
129 private:
130         std::string const filename_;
131         QFileSystemWatcher * qwatcher_;
132         bool exists_;
133 };
134
135
136 class FileMonitorBlockerGuard : public QObject
137 {
138         Q_OBJECT
139         QPointer<FileMonitor> monitor_;
140         int delay_;
141
142 public:
143         FileMonitorBlockerGuard(FileMonitor * monitor);
144         ~FileMonitorBlockerGuard();
145         void setDelay(int delay);
146 };
147
148
149 typedef std::shared_ptr<FileMonitorBlockerGuard> FileMonitorBlocker;
150
151
152 /// Main class
153 class FileMonitor : public QObject
154 {
155         Q_OBJECT
156         friend class FileMonitorBlockerGuard;
157
158 public:
159         FileMonitor(std::shared_ptr<FileMonitorGuard> monitor);
160
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()
166         void disconnect();
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(); }
180
181 Q_SIGNALS:
182         /// Connect to this to be notified when the file changes
183         void fileChanged() const;
184
185 protected Q_SLOTS:
186         /// Receive notifications from the FileMonitorGuard
187         void changed();
188         ///
189         void reconnectToFileMonitorGuard();
190
191 private:
192         /// boost signal
193         sig fileChanged_;
194         /// the unique watch for our file
195         std::shared_ptr<FileMonitorGuard> const monitor_;
196         ///
197         std::weak_ptr<FileMonitorBlockerGuard> blocker_;
198 };
199
200
201 /// When a more active monitoring style is needed.
202 /// For instance because QFileSystemWatcher does not work for remote file
203 /// systems.
204 class ActiveFileMonitor : public FileMonitor
205 {
206         Q_OBJECT
207 public:
208         ActiveFileMonitor(std::shared_ptr<FileMonitorGuard> monitor,
209                           FileName const & filename, int interval);
210         /// call checkModified asynchronously
211         void checkModifiedAsync();
212
213 public Q_SLOTS:
214         /// Check explicitly for a modification, but not more than once every
215         /// interval ms.
216         void checkModified();
217
218 private Q_SLOTS:
219         void setCooldown() { cooldown_ = true; }
220         void clearCooldown() { cooldown_ = false; }
221
222 private:
223         FileName const filename_;
224         ///
225         int const interval_;
226         ///
227         time_t timestamp_;
228         ///
229         unsigned long checksum_;
230         ///
231         bool cooldown_;
232 };
233
234
235 } // namespace support
236 } // namespace lyx
237
238 #endif // FILEMONITOR_H