X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fsupport%2FFileMonitor.h;h=09d85e81a11eb2ac3b2ab279c8ec550f340f791f;hb=f96d7a8b2cb45e98382e2d88f1156e74e7898f5e;hp=d0d3741d4a7262a025f29dab1217ad847f1550c1;hpb=2047ea5eb436d98d485bed672ed218f9bd708ce6;p=lyx.git diff --git a/src/support/FileMonitor.h b/src/support/FileMonitor.h index d0d3741d4a..09d85e81a1 100644 --- a/src/support/FileMonitor.h +++ b/src/support/FileMonitor.h @@ -4,7 +4,7 @@ * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * - * \author Angus Leeming + * \author Guillaume Munch * * Full author contact details are available in file CREDITS. * @@ -15,62 +15,177 @@ #ifndef FILEMONITOR_H #define FILEMONITOR_H +#include + +#include +#include + #include + namespace lyx { namespace support { class FileName; -class FileMonitor +/// +/// FileMonitor, a file monitor based on QFileSystemWatcher +/// + +class FileMonitor; +class FileMonitorGuard; +using FileMonitorPtr = std::unique_ptr; + +/// +/// Watch a file: +/// FileMonitorPtr monitor = FileSystemWatcher::monitor(file_with_path); +/// monitor.connect(...); //(using boost::signals2), or: +/// connect(monitor, SIGNAL(fileChanged()),...); // (using Qt) +/// +/// Remember that a unique_ptr is automatically deleted at the end of a scope if +/// it has not been moved, or when assigned. When that happens, the signal +/// object is deleted and therefore all the connections are closed. The file +/// ceases being tracked when all the monitors for a file have been deleted. +/// +/// Stop watching: +/// * as determined statically by the scope, or +/// * dynamically, using: +/// monitor = nullptr; +/// +/// Watch a different file: +/// monitor = FileSystemWatcher::monitor(file_with_path2); +/// monitor.connect(...); +/// (stops watching the first) +/// +/// Block notifications for the duration of a scope: +/// { +/// FileMonitorBlocker block = monitor.block(); +/// ... +/// } +/// +/// Reset connections: +/// monitor.disconnect(); +/// or the disconnect method of the connection object for the boost signal. +/// +class FileSystemWatcher +{ +public: + // as described above + static FileMonitorPtr monitor(FileName const & file_with_path); + // Output whether the paths tracked by qwatcher_ and the active + // FileMonitorGuards are in correspondence. + static void debug(); +private: + FileSystemWatcher(); + // A global instance is created automatically on first call to monitor + static FileSystemWatcher & instance(); + // Caches the monitor guards but allow them to be destroyed + std::map> store_; + // This class is a wrapper for QFileSystemWatcher + std::unique_ptr const qwatcher_; +}; + + +// Must be unique per path +// Ends the watch when deleted +class FileMonitorGuard : public QObject +{ + Q_OBJECT + +public: + /// Start the watch + FileMonitorGuard(std::string const & filename, + QFileSystemWatcher * qwatcher); + /// End the watch + ~FileMonitorGuard(); + /// absolute path being tracked + std::string const & filename() { return filename_; } + /// Make sure it is being monitored, after e.g. a deletion. See + /// . This is called + /// automatically. + /// \param new_file If true, emit fileChanged if the file exists and was + /// successfully added. + void refresh(bool new_file = false); + +Q_SIGNALS: + /// Connect to this to be notified when the file changes + void fileChanged() const; + +private Q_SLOTS: + /// Receive notifications from the QFileSystemWatcher + void notifyChange(QString const & path); + +private: + std::string const filename_; + QFileSystemWatcher * qwatcher_; +}; + + +class FileMonitorBlockerGuard : public QObject { + Q_OBJECT + FileMonitor * parent_; + int delay_; + public: - /** Once monitoring begins, the file will be monitored every - * interval ms. - * - * FIXME: rewrite and simplify using an encapsulation of QFileSystemWatcher. - */ - FileMonitor(FileName const & file_with_path, int interval); - - /// Destructor - ~FileMonitor(); - - /// - void reset(FileName const & file_with_path) const; - - /// - FileName const & filename() const; - - /// Begin monitoring the file - void start() const; - /// - void stop() const; - /// - bool monitoring() const; - - /** The checksum is recomputed whenever the file is modified. - * If the file is not being monitored, then the checksum will be - * recomputed each time this function is called. - */ - unsigned long checksum() const; + FileMonitorBlockerGuard(FileMonitor * parent); + ~FileMonitorBlockerGuard(); + void setDelay(int delay); +}; + + +using FileMonitorBlocker = std::shared_ptr; + +/// Main class +class FileMonitor : public QObject +{ + Q_OBJECT + friend class FileMonitorBlockerGuard; + +public: + FileMonitor(std::shared_ptr monitor); + + using sig = boost::signals2::signal; /// Connect and you'll be informed when the file has changed. - typedef boost::signals2::signal FileChangedSig; - typedef FileChangedSig::slot_type slot_type; - /// - boost::signals2::connection connect(slot_type const &) const; + boost::signals2::connection connect(sig::slot_type const &); + /// disconnect all slots connected to the boost signal fileChanged_ or to + /// the qt signal fileChanged() + void disconnect(); + /// absolute path being tracked + std::string const & filename() { return monitor_->filename(); } + /// Creates a guard that blocks notifications. Copyable. Notifications from + /// this monitor are blocked as long as there are copies around. + /// \param delay is the amount waited in ms after expiration of the guard + /// before reconnecting. This delay thing is to deal with asynchronous + /// notifications in a not so elegant fashion. But it can also be used to + /// slow down incoming events. + FileMonitorBlocker block(int delay = 0); + /// Make sure the good file is being monitored, after e.g. a move or a + /// deletion. See . This is + /// called automatically. + void refresh() { return monitor_->refresh(); } + +Q_SIGNALS: + /// Connect to this to be notified when the file changes + void fileChanged() const; + +private Q_SLOTS: + /// Receive notifications from the FileMonitorGuard + void changed(); private: - /// noncopyable - FileMonitor(FileMonitor const &); - void operator=(FileMonitor const &); - - /// Use the Pimpl idiom to hide the internals. - class Impl; - /// The pointer never changes although *pimpl_'s contents may. - Impl * const pimpl_; + void connectToFileMonitorGuard(); + // boost signal + sig fileChanged_; + // the unique watch for our file + std::shared_ptr const monitor_; + // + std::weak_ptr blocker_; }; + + } // namespace support } // namespace lyx