]> git.lyx.org Git - lyx.git/blob - src/support/FileMonitor.h
Nonsense for whoever insists on using gcc4.6 & qt4.8 in 2017
[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
23 #include <boost/signals2.hpp>
24
25
26 namespace lyx {
27 namespace support {
28
29 class FileName;
30
31 ///
32 ///  FileMonitor, a file monitor based on QFileSystemWatcher
33 ///
34
35 class FileMonitor;
36 class FileMonitorGuard;
37 typedef std::unique_ptr<FileMonitor> FileMonitorPtr;
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         /// nonsense introduced for when QT_VERSION < 0x050000, cannot be placed
119         /// between #ifdef
120         void refreshTrue() { refresh(true); }
121         /// nonsense introduced for when QT_VERSION < 0x050000, cannot be placed
122         /// between #ifdef
123         void refreshFalse() { refresh(false); }
124
125
126 private:
127         std::string const filename_;
128         QFileSystemWatcher * qwatcher_;
129 };
130
131
132 class FileMonitorBlockerGuard : public QObject
133 {
134         Q_OBJECT
135         FileMonitor * parent_;
136         int delay_;
137
138 public:
139         FileMonitorBlockerGuard(FileMonitor * parent);
140         ~FileMonitorBlockerGuard();
141         void setDelay(int delay);
142 };
143
144
145 typedef std::shared_ptr<FileMonitorBlockerGuard> FileMonitorBlocker;
146
147
148 /// Main class
149 class FileMonitor : public QObject
150 {
151         Q_OBJECT
152         friend class FileMonitorBlockerGuard;
153
154 public:
155         FileMonitor(std::shared_ptr<FileMonitorGuard> monitor);
156
157         typedef boost::signals2::signal<void()> sig;
158         /// Connect and you'll be informed when the file has changed.
159         boost::signals2::connection connect(sig::slot_type const &);
160         /// disconnect all slots connected to the boost signal fileChanged_ or to
161         /// the qt signal fileChanged()
162         void disconnect();
163         /// absolute path being tracked
164         std::string const & filename() { return monitor_->filename(); }
165         /// Creates a guard that blocks notifications. Copyable. Notifications from
166         /// this monitor are blocked as long as there are copies around.
167         /// \param delay is the amount waited in ms after expiration of the guard
168         /// before reconnecting. This delay thing is to deal with asynchronous
169         /// notifications in a not so elegant fashion. But it can also be used to
170         /// slow down incoming events.
171         FileMonitorBlocker block(int delay = 0);
172         /// Make sure the good file is being monitored, after e.g. a move or a
173         /// deletion. See <https://bugreports.qt.io/browse/QTBUG-46483>. This is
174         /// called automatically.
175         void refresh() { return monitor_->refresh(); }
176
177 Q_SIGNALS:
178         /// Connect to this to be notified when the file changes
179         void fileChanged() const;
180
181 private Q_SLOTS:
182         /// Receive notifications from the FileMonitorGuard
183         void changed();
184         ///
185         void connectToFileMonitorGuard();
186
187 private:
188         // boost signal
189         sig fileChanged_;
190         // the unique watch for our file
191         std::shared_ptr<FileMonitorGuard> const monitor_;
192         //
193         std::weak_ptr<FileMonitorBlockerGuard> blocker_;
194 };
195
196
197
198 } // namespace support
199 } // namespace lyx
200
201 #endif // FILEMONITOR_H