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