]> git.lyx.org Git - lyx.git/blob - src/support/FileMonitor2.h
Replace FileMonitor with FileMonitor2 in RenderPreview
[lyx.git] / src / support / FileMonitor2.h
1 // -*- C++ -*-
2 /**
3  * \file FileMonitor2.h
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Guillaume Munch
8  *
9  * Full author contact details are available in file CREDITS.
10  *
11  * FileMonitor monitors a file and informs a listener when that file has
12  * changed.
13  */
14
15 #ifndef FILEMONITOR2_H
16 #define FILEMONITOR2_H
17
18 #include <memory>
19
20 #include <QFileSystemWatcher>
21 #include <QObject>
22
23 #include <boost/signals2.hpp>
24
25
26 namespace lyx {
27 namespace support {
28
29 class FileName;
30
31 ///
32 ///  FileMonitor2, a file monitor based on QFileSystemWatcher
33 ///
34
35 class FileMonitor2;
36 class FileMonitorGuard;
37 using FileMonitorPtr = std::unique_ptr<FileMonitor2>;
38
39 ///
40 /// Watch a file:
41 ///   FileMonitorPtr monitor = FileSystemWatcher::monitor(file_with_path);
42 ///   monitor.connect(...); //(using boost::signals2), or:
43 ///   connect(monitor, SIGNAL(fileChanged()),...); // (using Qt)
44 ///
45 /// Remember that a unique_ptr is automatically deleted at the end of a scope if
46 /// it has not been moved, or when assigned. When that happens, the signal
47 /// object is deleted and therefore all the connections are closed. The file
48 /// ceases being tracked when all the monitors for a file have been deleted.
49 ///
50 /// Stop watching:
51 ///   * as determined statically by the scope, or
52 ///   * dynamically, using:
53 ///       monitor = nullptr;
54 ///
55 /// Watch a different file:
56 ///   monitor = FileSystemWatcher::monitor(file_with_path2);
57 ///   monitor.connect(...);
58 /// (stops watching the first)
59 ///
60 /// Block notifications for the duration of a scope:
61 ///   {
62 ///       FileMonitorBlocker block = monitor.block();
63 ///       ...
64 ///   }
65 ///
66 /// Reset connections:
67 ///   monitor.disconnect();
68 ///   or the disconnect method of the connection object for the boost signal.
69 ///
70 class FileSystemWatcher
71 {
72 public:
73         // as described above
74         static FileMonitorPtr monitor(FileName const & file_with_path);
75         // Output whether the paths tracked by qwatcher_ and the active
76         // FileMonitorGuards are in correspondence.
77         static void debug();
78 private:
79         FileSystemWatcher();
80         // A global instance is created automatically on first call to monitor
81         static FileSystemWatcher & instance();
82         // Caches the monitor guards but allow them to be destroyed
83         std::map<std::string, std::weak_ptr<FileMonitorGuard>> store_;
84         // This class is a wrapper for QFileSystemWatcher
85         std::unique_ptr<QFileSystemWatcher> const qwatcher_;
86 };
87
88
89 // Must be unique per path
90 // Ends the watch when deleted
91 class FileMonitorGuard : public QObject
92 {
93         Q_OBJECT
94
95 public:
96         /// Start the watch
97         FileMonitorGuard(std::string const & filename,
98                          QFileSystemWatcher * qwatcher);
99         /// End the watch
100         ~FileMonitorGuard();
101         /// absolute path being tracked
102         std::string const & filename() { return filename_; }
103         /// Make sure it is being monitored, after e.g. a deletion. See
104         /// <https://bugreports.qt.io/browse/QTBUG-46483>. This is called
105         /// automatically.
106         /// \param new_file  If true, emit fileChanged if the file exists and was
107         /// successfully added.
108         void refresh(bool new_file = false);
109
110 Q_SIGNALS:
111         /// Connect to this to be notified when the file changes
112         void fileChanged() const;
113
114 private Q_SLOTS:
115         /// Receive notifications from the QFileSystemWatcher
116         void notifyChange(QString const & path);
117
118 private:
119         std::string const filename_;
120         QFileSystemWatcher * qwatcher_;
121 };
122
123
124 class FileMonitorBlockerGuard : public QObject
125 {
126         Q_OBJECT
127         FileMonitor2 * parent_;
128         int delay_;
129
130 public:
131         FileMonitorBlockerGuard(FileMonitor2 * parent);
132         ~FileMonitorBlockerGuard();
133         void setDelay(int delay);
134 };
135
136
137 using FileMonitorBlocker = std::shared_ptr<FileMonitorBlockerGuard>;
138
139
140 /// Main class
141 class FileMonitor2 : public QObject
142 {
143         Q_OBJECT
144         friend class FileMonitorBlockerGuard;
145
146 public:
147         FileMonitor2(std::shared_ptr<FileMonitorGuard> monitor);
148
149         using sig = boost::signals2::signal<void()>;
150         /// Connect and you'll be informed when the file has changed.
151         boost::signals2::connection connect(sig::slot_type const &);
152         /// disconnect all slots connected to the boost signal fileChanged_ or to
153         /// the qt signal fileChanged()
154         void disconnect();
155         /// absolute path being tracked
156         std::string const & filename() { return monitor_->filename(); }
157         /// Creates a guard that blocks notifications. Copyable. Notifications from
158         /// this monitor are blocked as long as there are copies around.
159         /// \param delay is the amount waited in ms after expiration of the guard
160         /// before reconnecting. This delay thing is to deal with asynchronous
161         /// notifications in a not so elegant fashion. But it can also be used to
162         /// slow down incoming events.
163         FileMonitorBlocker block(int delay = 0);
164         /// Make sure the good file is being monitored, after e.g. a move or a
165         /// deletion. See <https://bugreports.qt.io/browse/QTBUG-46483>. This is
166         /// called automatically.
167         void refresh() { return monitor_->refresh(); }
168
169 Q_SIGNALS:
170         /// Connect to this to be notified when the file changes
171         void fileChanged() const;
172
173 private Q_SLOTS:
174         /// Receive notifications from the FileMonitorGuard
175         void changed();
176
177 private:
178         void connectToFileMonitorGuard();
179         // boost signal
180         sig fileChanged_;
181         // the unique watch for our file
182         std::shared_ptr<FileMonitorGuard> const monitor_;
183         //
184         std::weak_ptr<FileMonitorBlockerGuard> blocker_;
185 };
186
187
188
189 } // namespace support
190 } // namespace lyx
191
192 #endif // FILEMONITOR2_H