]> git.lyx.org Git - lyx.git/blob - src/support/FileMonitor.h
14aa834c80c6a556a09fb2a73ae96f92999f5154
[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 #include "support/signals.h"
21
22 #include <memory>
23
24 #include <QFileSystemWatcher>
25 #include <QObject>
26 #include <QPointer>
27
28
29 namespace lyx {
30 namespace support {
31
32 ///
33 ///  FileMonitor, a file monitor based on QFileSystemWatcher
34 ///
35
36 class FileMonitor;
37 class ActiveFileMonitor;
38 class FileMonitorGuard;
39 typedef std::unique_ptr<FileMonitor> FileMonitorPtr;
40 typedef std::unique_ptr<ActiveFileMonitor> ActiveFileMonitorPtr;
41
42 ///
43 /// Watch a file:
44 ///   FileMonitorPtr monitor = FileSystemWatcher::monitor(file_with_path);
45 ///   monitor.connect(...); //(using boost::signals2), or:
46 ///   connect(monitor, SIGNAL(fileChanged()),...); // (using Qt)
47 ///
48 /// Remember that a unique_ptr is automatically deleted at the end of a scope if
49 /// it has not been moved, or when assigned. When that happens, the signal
50 /// object is deleted and therefore all the connections are closed. The file
51 /// ceases being tracked when all the monitors for a file have been deleted.
52 ///
53 /// Stop watching:
54 ///   * as determined statically by the scope, or
55 ///   * dynamically, using:
56 ///       monitor = nullptr;
57 ///
58 /// Watch a different file:
59 ///   monitor = FileSystemWatcher::monitor(file_with_path2);
60 ///   monitor.connect(...);
61 /// (stops watching the first)
62 ///
63 /// Reset connections:
64 ///   monitor.disconnect();
65 ///   or the disconnect method of the connection object for the boost signal.
66 ///
67 class FileSystemWatcher
68 {
69 public:
70         /// as described above
71         static FileMonitorPtr monitor(FileName const & filename);
72         /// same but with an ActiveFileMonitor
73         static ActiveFileMonitorPtr activeMonitor(FileName const & filename,
74                                                   int interval = 10000);
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
81         static FileSystemWatcher & instance();
82         ///
83         std::shared_ptr<FileMonitorGuard> getGuard(FileName const & filename);
84         /// Caches the monitor guards but allow them to be destroyed
85         std::map<std::string, std::weak_ptr<FileMonitorGuard>> store_;
86         /// This class is a wrapper for QFileSystemWatcher
87         std::unique_ptr<QFileSystemWatcher> const qwatcher_;
88 };
89
90
91 /// Must be unique per path
92 /// Ends the watch when deleted
93 class FileMonitorGuard : public QObject
94 {
95         Q_OBJECT
96
97 public:
98         /// Start the watch
99         FileMonitorGuard(std::string const & filename,
100                          QFileSystemWatcher * qwatcher);
101         /// End the watch
102         ~FileMonitorGuard();
103         /// absolute path being tracked
104         std::string const & filename() { return filename_; }
105
106 public Q_SLOTS:
107         /// Make sure it is being monitored, after e.g. a deletion. See
108         /// <https://bugreports.qt.io/browse/QTBUG-46483>. This is called
109         /// automatically.
110         void refresh(bool emit = true);
111
112 Q_SIGNALS:
113         /// Connect to this to be notified when the file changes
114         void fileChanged(bool exists) const;
115
116 private Q_SLOTS:
117         /// Receive notifications from the QFileSystemWatcher
118         void notifyChange(QString const & path);
119
120 private:
121         std::string const filename_;
122         QFileSystemWatcher * qwatcher_;
123         /// for emitting fileChanged() when the file is created or deleted
124         bool exists_;
125 };
126
127
128 /// Main class
129 class FileMonitor : public QObject
130 {
131         Q_OBJECT
132
133 public:
134         FileMonitor(std::shared_ptr<FileMonitorGuard> monitor);
135
136         typedef signals2::signal<void(bool)> sig;
137         typedef sig::slot_type slot;
138         /// Connect and you'll be informed when the file has changed.
139         signals2::connection connect(slot const &);
140         /// disconnect all slots connected to the boost signal fileChanged_ or to
141         /// the qt signal fileChanged()
142         void disconnect();
143         /// absolute path being tracked
144         std::string const & filename() { return monitor_->filename(); }
145         /// Make sure the good file is being monitored, after e.g. a move or a
146         /// deletion. See <https://bugreports.qt.io/browse/QTBUG-46483>. This is
147         /// called automatically.
148         void refresh() { monitor_->refresh(); }
149
150 Q_SIGNALS:
151         /// Connect to this to be notified when the file changes
152         void fileChanged(bool exists) const;
153
154 protected Q_SLOTS:
155         /// Receive notifications from the FileMonitorGuard
156         void changed(bool exists);
157         ///
158         void connectToFileMonitorGuard();
159
160 private:
161         /// boost signal
162         sig fileChanged_;
163         /// the unique watch for our file
164         std::shared_ptr<FileMonitorGuard> const monitor_;
165 };
166
167
168 /// When a more active monitoring style is needed.
169 /// For instance because QFileSystemWatcher does not work for remote file
170 /// systems.
171 class ActiveFileMonitor : public FileMonitor
172 {
173         Q_OBJECT
174 public:
175         ActiveFileMonitor(std::shared_ptr<FileMonitorGuard> monitor,
176                           FileName const & filename, int interval);
177         /// call checkModified asynchronously
178         void checkModifiedAsync();
179
180 public Q_SLOTS:
181         /// Check explicitly for a modification, but not more than once every
182         /// interval ms.
183         void checkModified();
184
185 private Q_SLOTS:
186         void setCooldown() { cooldown_ = true; }
187         void clearCooldown() { cooldown_ = false; }
188
189 private:
190         FileName const filename_;
191         ///
192         int const interval_;
193         ///
194         time_t timestamp_;
195         ///
196         unsigned long checksum_;
197         ///
198         bool cooldown_;
199 };
200
201
202 } // namespace support
203 } // namespace lyx
204
205 #endif // FILEMONITOR_H