]> git.lyx.org Git - features.git/blob - src/support/FileMonitor.h
Revert "WIP: refactor Systemcall"
[features.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 #include <map>
24
25 #include <QFileSystemWatcher>
26 #include <QObject>
27 #include <QPointer>
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 class FileSystemWatcher
65 {
66 public:
67         /// as described above
68         static FileMonitorPtr monitor(FileName const & filename);
69         /// same but with an ActiveFileMonitor
70         static ActiveFileMonitorPtr activeMonitor(FileName const & filename,
71                                                   int interval = 10000);
72         /// Output whether the paths tracked by qwatcher_ and the active
73         /// FileMonitorGuards are in correspondence.
74         static void debug();
75 private:
76         FileSystemWatcher();
77         /// A global instance is created automatically on first call
78         static FileSystemWatcher & instance();
79         ///
80         std::shared_ptr<FileMonitorGuard> getGuard(FileName const & filename);
81         /// Caches the monitor guards but allow them to be destroyed
82         std::map<std::string, std::weak_ptr<FileMonitorGuard>> store_;
83         /// This class is a wrapper for QFileSystemWatcher
84         std::unique_ptr<QFileSystemWatcher> const qwatcher_;
85 };
86
87
88 /// Must be unique per path
89 /// Ends the watch when deleted
90 class FileMonitorGuard : public QObject
91 {
92         Q_OBJECT
93
94 public:
95         /// Start the watch
96         FileMonitorGuard(std::string const & filename,
97                          QFileSystemWatcher * qwatcher);
98         /// End the watch
99         ~FileMonitorGuard();
100         /// absolute path being tracked
101         std::string const & filename() { return filename_; }
102
103 public Q_SLOTS:
104         /// Make sure it is being monitored, after e.g. a deletion. See
105         /// <https://bugreports.qt.io/browse/QTBUG-46483>. This is called
106         /// automatically.
107         void refresh(bool emit = true);
108
109 Q_SIGNALS:
110         /// Connect to this to be notified when the file changes
111         void fileChanged(bool exists) const;
112
113 private Q_SLOTS:
114         /// Receive notifications from the QFileSystemWatcher
115         void notifyChange(QString const & path);
116
117 private:
118         std::string const filename_;
119         QFileSystemWatcher * qwatcher_;
120         /// for emitting fileChanged() when the file is created or deleted
121         bool exists_;
122 };
123
124
125 /// Main class
126 class FileMonitor : public QObject
127 {
128         Q_OBJECT
129
130 public:
131         FileMonitor(std::shared_ptr<FileMonitorGuard> monitor);
132
133         typedef signal<void(bool)> sig;
134         typedef sig::slot_type slot;
135         /// Connect and you'll be informed when the file has changed.
136         connection connect(slot const &);
137         /// absolute path being tracked
138         std::string const & filename() { return monitor_->filename(); }
139         /// Make sure the good file is being monitored, after e.g. a move or a
140         /// deletion. See <https://bugreports.qt.io/browse/QTBUG-46483>. This is
141         /// called automatically.
142         void refresh() { monitor_->refresh(); }
143
144 Q_SIGNALS:
145         /// Connect to this to be notified when the file changes
146         void fileChanged(bool exists) const;
147
148 protected Q_SLOTS:
149         /// Receive notifications from the FileMonitorGuard
150         void changed(bool exists);
151         ///
152         void connectToFileMonitorGuard();
153
154 private:
155         /// boost signal
156         sig fileChanged_;
157         /// the unique watch for our file
158         std::shared_ptr<FileMonitorGuard> const monitor_;
159 };
160
161
162 /// When a more active monitoring style is needed.
163 /// For instance because QFileSystemWatcher does not work for remote file
164 /// systems.
165 class ActiveFileMonitor : public FileMonitor
166 {
167         Q_OBJECT
168 public:
169         ActiveFileMonitor(std::shared_ptr<FileMonitorGuard> monitor,
170                           FileName const & filename, int interval);
171         /// call checkModified asynchronously
172         void checkModifiedAsync();
173
174 public Q_SLOTS:
175         /// Check explicitly for a modification, but not more than once every
176         /// interval ms.
177         void checkModified();
178
179 private Q_SLOTS:
180         void setCooldown() { cooldown_ = true; }
181         void clearCooldown() { cooldown_ = false; }
182
183 private:
184         FileName const filename_;
185         ///
186         int const interval_;
187         ///
188         time_t timestamp_;
189         ///
190         unsigned long checksum_;
191         ///
192         bool cooldown_;
193 };
194
195
196 } // namespace support
197 } // namespace lyx
198
199 #endif // FILEMONITOR_H