]> git.lyx.org Git - features.git/commitdiff
File monitoring is Go!
authorAngus Leeming <leeming@lyx.org>
Thu, 18 Jul 2002 14:01:42 +0000 (14:01 +0000)
committerAngus Leeming <leeming@lyx.org>
Thu, 18 Jul 2002 14:01:42 +0000 (14:01 +0000)
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@4703 a592a061-630c-0410-9148-cb99ea01b6c8

12 files changed:
src/graphics/ChangeLog
src/graphics/GraphicsCacheItem.C
src/graphics/GraphicsCacheItem.h
src/graphics/GraphicsLoader.C
src/graphics/GraphicsLoader.h
src/insets/ChangeLog
src/insets/insetgraphics.C
src/stamp-h.in
src/support/ChangeLog
src/support/FileMonitor.C [new file with mode: 0644]
src/support/FileMonitor.h [new file with mode: 0644]
src/support/Makefile.am

index 7454502cb2a4e6cfa0a9f5b0c7edc7131b8c13df..671055c217670db2364650b1bf1fe6738852c570 100644 (file)
@@ -1,3 +1,15 @@
+2002-07-18  Angus Leeming  <leeming@lyx.org>
+
+       * GraphicsCacheItem.[Ch]: add a FileMonitor variable to the the Impl
+       class.
+       (startMonitoring, monitoring, checksum): new methods to interact with
+       the FileMonitor.
+
+       * GraphicsLoader.[Ch] (startMonitoring, monitoring, checksum): new
+       methods invoking the CacheItem methods of the same name.
+       (resetFile): if monitoring and the file changes, start monitoring this
+       new file.
+
 2002-07-17  Jean-Marc Lasgouttes  <lasgouttes@freesurf.fr>
 
        * Makefile.am: remove FileMonitor.[Ch]
index 44dd44c867ac7fb11635f40ab728658faa2d4558..95092e42d13ef411a144da260858e7417347654b 100644 (file)
@@ -18,6 +18,8 @@
 #include "GraphicsImage.h"
 #include "GraphicsConverter.h"
 
+#include "support/FileMonitor.h"
+
 #include "debug.h"
 
 #include "support/LAssert.h"
@@ -73,8 +75,22 @@ struct CacheItem::Impl : public boost::signals::trackable {
         */
        void setStatus(ImageStatus new_status);
 
+       /** Can be invoked directly by the user, but is also connected to the
+        *  FileMonitor and so is invoked when the file is changed
+        *  (if monitoring is taking place).
+        */
+       void startLoading();
+
+       /** If we are asked to load the file for a second or further time,
+        *  (because the file has changed), then we'll have to first reset
+        *  many of the variables below.
+        */
+       void reset();
+
        /// The filename we refer too.
        string const filename_;
+       ///
+       FileMonitor const monitor_;
 
        /// Is the file compressed?
        bool zipped_;
@@ -123,10 +139,26 @@ string const & CacheItem::filename() const
 
 void CacheItem::startLoading() const
 {
-       if (pimpl_->status_ != WaitingToLoad)
-               return;
+       pimpl_->startLoading();
+}
+
+
+void CacheItem::startMonitoring() const
+{
+       if (!pimpl_->monitor_.monitoring())
+               pimpl_->monitor_.start();
+}
 
-       pimpl_->convertToDisplayFormat();
+
+bool CacheItem::monitoring() const
+{
+       return pimpl_->monitor_.monitoring();
+}
+
+
+unsigned long CacheItem::checksum() const
+{
+       return pimpl_->monitor_.checksum();
 }
 
 
@@ -154,9 +186,51 @@ boost::signals::connection CacheItem::connect(slot_type const & slot) const
 
 
 CacheItem::Impl::Impl(string const & file)
-       : filename_(file), zipped_(false),
-         remove_loaded_file_(false), status_(WaitingToLoad)
-{}
+       : filename_(file),
+         monitor_(file, 2000),
+         zipped_(false),
+         remove_loaded_file_(false),
+         status_(WaitingToLoad)
+{
+       monitor_.connect(boost::bind(&Impl::startLoading, this));
+}
+
+
+void CacheItem::Impl::startLoading()
+{
+       if (status_ != WaitingToLoad)
+               reset();
+
+       convertToDisplayFormat();
+}
+
+
+void CacheItem::Impl::reset()
+{
+       zipped_ = false;
+       if (!unzipped_filename_.empty())
+               lyx::unlink(unzipped_filename_);
+       unzipped_filename_.clear();
+
+       if (remove_loaded_file_ && !file_to_load_.empty())
+               lyx::unlink(file_to_load_);
+       remove_loaded_file_ = false;
+       file_to_load_.clear();
+
+       if (image_.get())
+               image_.reset();
+
+       status_ = WaitingToLoad;
+
+       if (cl_.connected())
+               cl_.disconnect();
+
+       if (cc_.connected())
+               cc_.disconnect();
+
+       if (converter_.get())
+               converter_.reset();
+}
 
 
 void CacheItem::Impl::setStatus(ImageStatus new_status)
@@ -276,6 +350,17 @@ namespace grfx {
 void CacheItem::Impl::convertToDisplayFormat()
 {
        setStatus(Converting);
+
+       // First, check that the file exists!
+       if (!IsFileReadable(filename_)) {
+               if (status_ != ErrorNoFile) {
+                       setStatus(ErrorNoFile);
+                       lyxerr[Debug::GRAPHICS]
+                               << "\tThe file is not readable" << endl;
+               }
+               return;
+       }
+
        // Make a local copy in case we unzip it
        string const filename = zippedFile(filename_) ?
                unzipFile(filename_) : filename_;
@@ -285,13 +370,6 @@ void CacheItem::Impl::convertToDisplayFormat()
                << "\n\twith displayed filename: " << displayed_filename
                << endl;
 
-       // First, check that the file exists!
-       if (!IsFileReadable(filename)) {
-               setStatus(ErrorNoFile);
-               lyxerr[Debug::GRAPHICS] << "\tThe file is not readable" << endl;
-               return;
-       }
-
        string from = getExtFromContents(filename);
        lyxerr[Debug::GRAPHICS]
                << "\n\tThe file contains " << from << " format data." << endl;
index 15f441a4c05166012512fe8993869810b75cc4db..bc80bd282dda59d144bbf5ae66efb542fb18da36 100644 (file)
@@ -59,6 +59,19 @@ public:
        /// It's in the cache. Now start the loading process.
        void startLoading() const;
 
+       /** Monitor any changes to the file.
+        *  There is no point monitoring the file before startLoading() is
+        *  invoked.
+        */
+       void startMonitoring() const;
+       ///
+       bool monitoring() const;
+       /** Returns the check sum of filename() so that, for example, you can
+        *  ascertain whether to output a new PostScript version of the file
+        *  for a LaTeX run.
+        */
+       unsigned long checksum() const;
+
        /** Get the image associated with filename().
         *  If the image is not yet loaded, returns 0.
         *  This routine returns a pointer to const; if you want to modify it,
index 6bbd507befbb192d28f6300c46c66b749f9253cb..c819f4379fef9ab33960e81dea240209ac2d755c 100644 (file)
@@ -139,6 +139,33 @@ void Loader::startLoading(Inset const & inset, BufferView const & bv) const
 }
 
 
+void Loader::startMonitoring() const
+{
+       if (!pimpl_->cached_item_.get())
+               return;
+
+       pimpl_->cached_item_->startMonitoring();
+}
+
+
+bool Loader::monitoring() const
+{
+       if (!pimpl_->cached_item_.get())
+               return false;
+
+       return pimpl_->cached_item_->monitoring();
+}
+
+
+unsigned long Loader::checksum() const
+{
+       if (!pimpl_->cached_item_.get())
+               return 0;
+
+       return pimpl_->cached_item_->checksum();
+}
+
+
 string const & Loader::filename() const
 {
        static string const empty;
@@ -187,7 +214,12 @@ void Loader::Impl::resetFile(string const & file)
        if (file == old_file)
                return;
 
+       // If monitoring() the current file, should continue to monitor the
+       // new file.
+       bool continue_monitoring = false;
+
        if (!old_file.empty()) {
+               continue_monitoring = cached_item_->monitoring();
                cached_item_.reset();
                Cache::get().remove(old_file);
        }
@@ -206,6 +238,9 @@ void Loader::Impl::resetFile(string const & file)
        cached_item_ = gc.item(file);
        status_ = cached_item_->status();
 
+       if (continue_monitoring && !cached_item_->monitoring())
+               cached_item_->startMonitoring();
+
        cached_item_->connect(boost::bind(&Impl::statusChanged, this));
 }
 
@@ -231,7 +266,7 @@ void Loader::Impl::statusChanged()
 
 void Loader::Impl::createPixmap()
 {
-       if (!cached_item_.get() || image_.get() ||
+       if (!cached_item_.get() ||
            params_.display == NoDisplay || status_ != Loaded)
                return;
 
index f0959daf73650f01f223250410ae64241246bb10..9ac9e458887d3a06eb9151194a73525f764137b0 100644 (file)
@@ -73,6 +73,19 @@ public:
         */
        void startLoading(Inset const &, BufferView const &) const;
 
+       /** Monitor any changes to the file.
+        *  There is no point monitoring the file before startLoading() is
+        *  invoked.
+        */
+       void startMonitoring() const;
+       ///
+       bool monitoring() const;
+       /** Returns the check sum of filename() so that, for example, you can
+        *  ascertain whether to output a new PostScript version of the file
+        *  for a LaTeX run.
+        */
+       unsigned long checksum() const;
+
        /// How far have we got in loading the image?
        ImageStatus status() const;
 
index baf2e8393302aba635e926f475cf17d15fddef4d..f0f953a2de22a7ab33de81a1ded1ab287ac2aa9e 100644 (file)
@@ -1,3 +1,10 @@
+2002-07-18  Angus Leeming  <leeming@lyx.org>
+
+       * insetgraphics.C: clean-up comments (from Herbert).
+       add a new checksum variable to the cache. Use it in PrepareFile.
+       (draw): start monitoring the file for a change.
+       (prepareFile): re-arrange a little to avoid unnecessary steps.
+
 2002-07-17  Angus Leeming  <leeming@lyx.org>
 
        * insetgraphics.C (Cache c-tor): bind to the GraphicsLoader through
index 7cad1907e90471277e6568820d4e62212571b7d7..424fef4eb24b88addf2849bd405b07c320546b29 100644 (file)
  * ====================================================== */
 
 /*
-Known BUGS:
-
-    * If the image is from the clipart, and the document is moved to another
-      directory, the user is screwed. Need a way to handle it.
-      This amounts to a problem of when to use relative or absolute file paths
-      We should probably use what the user asks to use... but when he chooses
-      by the file dialog we normally get an absolute path and this may not be
-      what the user meant.
-
-      Note that browseRelFile in helper_funcs.* provides a file name
-      which is relative if it is at reference path (here puffer path)
-      level or below, and an absolute path if the file name is not a
-      `natural' relative file name. In any case,
-             MakeAbsPath(filename, buf->filePath())
-      is guaranteed to provide the correct absolute path. This is what is
-      done know for include insets. Feel free to ask me -- JMarc
-      14/01/2002
-
-TODO Before initial production release:
+TODO
 
     * What advanced features the users want to do?
       Implement them in a non latex dependent way, but a logical way.
@@ -162,6 +144,8 @@ struct InsetGraphics::Cache : boost::signals::trackable
        int old_ascent;
        ///
        grfx::Loader loader;
+       ///
+       unsigned long checksum;
 
 private:
        ///
@@ -170,7 +154,7 @@ private:
 
 
 InsetGraphics::Cache::Cache(InsetGraphics & p)
-       : old_ascent(0), parent_(p)
+       : old_ascent(0), checksum(0), parent_(p)
 {
        loader.connect(boost::bind(&InsetGraphics::statusChanged, &parent_));
 }
@@ -327,9 +311,11 @@ void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
        int old_x = int(x);
        x += lwidth;
 
-       if (cache_->loader.status() == grfx::WaitingToLoad) {
+       if (cache_->loader.status() == grfx::WaitingToLoad)
                cache_->loader.startLoading(*this, *bv);
-       }
+
+       if (!cache_->loader.monitoring())
+               cache_->loader.startMonitoring();
 
        // This will draw the graphics. If the graphics has not been loaded yet,
        // we draw just a rectangle.
@@ -626,41 +612,39 @@ string const InsetGraphics::prepareFile(Buffer const *buf) const
        if (!IsFileReadable(orig_file_with_path))
                return orig_file;
 
-       // If the file is compressed and we have specified that it should not be
-       // uncompressed, then just return its name and let LaTeX do the rest!
-
-       // maybe that other zip extensions also be useful, especially the
-       // ones that may be declared in texmf/tex/latex/config/graphics.cfg.
-       // for example:
-       /* -----------snip-------------
-         {\DeclareGraphicsRule{.pz}{eps}{.bb}{}%
-          \DeclareGraphicsRule{.eps.Z}{eps}{.eps.bb}{}%
-          \DeclareGraphicsRule{.ps.Z}{eps}{.ps.bb}{}%
-          \DeclareGraphicsRule{.ps.gz}{eps}{.ps.bb}{}%
-          \DeclareGraphicsRule{.eps.gz}{eps}{.eps.bb}{}}}%
-        -----------snip-------------*/
-
        bool const zipped = zippedFile(orig_file_with_path);
-       if (zipped)
-               lyxerr[Debug::GRAPHICS] << "\twe have a zipped file ("
-                       << getExtFromContents(orig_file_with_path) << ")\n";
-       if (params().noUnzip && zipped) {
+
+       // If the file is compressed and we have specified that it
+       // should not be uncompressed, then just return its name and
+       // let LaTeX do the rest!
+       if (zipped && params().noUnzip) {
                lyxerr[Debug::GRAPHICS]
-                       << "\tpass file unzipped to LaTeX but with full path.\n";
-               // latex needs an absolue path, otherwise the coresponding
-               // *.eps.bb file isn't found
+                       << "\tpass zipped file to LaTeX but with full path.\n";
+               // LaTeX needs an absolue path, otherwise the
+               // coresponding *.eps.bb file isn't found
                return orig_file_with_path;
        }
 
+       // Ascertain whether the file has changed.
+       unsigned long const new_checksum = cache_->loader.checksum();
+       bool const file_has_changed = cache_->checksum != new_checksum;
+       if (file_has_changed)
+               cache_->checksum = new_checksum;
+
+       // temp_file will contain the file for LaTeX to act on if, for example,
+       // we move it to a temp dir or uncompress it.
        string temp_file(orig_file);
-       // Uncompress the file if necessary. If it has been uncompressed in
-       // a previous call to prepareFile, do nothing.
+
        if (zipped) {
+               // Uncompress the file if necessary.
+               // If it has been uncompressed in a previous call to
+               // prepareFile, do nothing.
                temp_file = MakeAbsPath(OnlyFilename(temp_file), buf->tmppath);
                lyxerr[Debug::GRAPHICS]
                        << "\ttemp_file: " << temp_file << endl;
-               if (!IsFileReadable(temp_file)) {
-                       bool const success = lyx::copy(orig_file_with_path, temp_file);
+               if (file_has_changed || !IsFileReadable(temp_file)) {
+                       bool const success = lyx::copy(orig_file_with_path,
+                                                      temp_file);
                        lyxerr[Debug::GRAPHICS]
                                << "\tCopying zipped file from "
                                << orig_file_with_path << " to " << temp_file
@@ -673,16 +657,17 @@ string const InsetGraphics::prepareFile(Buffer const *buf) const
                lyxerr[Debug::GRAPHICS]
                        << "\tunzipped to " << orig_file_with_path << endl;
        }
+
        string const from = getExtFromContents(orig_file_with_path);
+       string const to   = findTargetFormat(from);
+       lyxerr[Debug::GRAPHICS]
+               << "\t we have: from " << from << " to " << to << '\n';
 
-       // "nice" means that the buffer is exported to LaTeX format but not
-       //        run through the LaTeX compiler.
-       // if (nice)
-       //      no conversion needed!
-       //      Return the original filename as is, because we do not know
-       //      what the user decide.
-       if (buf->niceFile)
-               return orig_file;
+       if (from == to && !lyxrc.use_tempdir)
+               // No conversion is needed. LaTeX can handle the
+               // graphic file as is.
+               // This is true even if the orig_file is compressed.
+               return RemoveExtension(orig_file_with_path);
 
        // We're going to be running the exported buffer through the LaTeX
        // compiler, so must ensure that LaTeX can cope with the graphics
@@ -711,8 +696,8 @@ string const InsetGraphics::prepareFile(Buffer const *buf) const
                lyxerr[Debug::GRAPHICS]
                        << "\tchanged to: " << temp_file << endl;
 
-               // if the file doen't exists, copy it into the tempdi
-               if (!IsFileReadable(temp_file)) {
+               // if the file doen't exists, copy it into the tempdir
+               if (file_has_changed || !IsFileReadable(temp_file)) {
                        bool const success = lyx::copy(orig_file_with_path, temp_file);
                        lyxerr[Debug::GRAPHICS]
                                << "\tcopying from " << orig_file_with_path << " to "
@@ -724,18 +709,11 @@ string const InsetGraphics::prepareFile(Buffer const *buf) const
                                return orig_file;
                        }
                }
-       }
 
-       string const to = findTargetFormat(from);
-       lyxerr[Debug::GRAPHICS]
-               << "\t we have: from " << from << " to " << to << '\n';
-       if (from == to) {
-               // No conversion is needed. LaTeX can handle the graphic file as is.
-               // This is true even if the orig_file is compressed. We have to return
-               // the orig_file_with_path, maybe it is a zipped one
-               if (lyxrc.use_tempdir)
+               if (from == to)
+                       // No conversion is needed. LaTeX can handle the
+                       // graphic file as is.
                        return RemoveExtension(temp_file);
-               return RemoveExtension(orig_file_with_path);
        }
 
        string const outfile_base = RemoveExtension(temp_file);
@@ -821,6 +799,14 @@ int InsetGraphics::latex(Buffer const *buf, ostream & os,
                << "\tBefore = " << before
                << "\n\tafter = " << after << endl;
 
+
+       // "nice" means that the buffer is exported to LaTeX format but not
+       //        run through the LaTeX compiler.
+       if (buf->niceFile) {
+               os << before <<'{' << params().filename << '}' << after;
+               return 1;
+       }
+
        // Make the filename relative to the lyx file
        // and remove the extension so the LaTeX will use whatever is
        // appropriate (when there are several versions in different formats)
index 9788f70238c91894045d22366fa941580826c3c1..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1 +0,0 @@
-timestamp
index 360d90e932a33e9ac1269d54296f5e2b7bd5e4ff..69d62acebfcdfdce72d75562ff233c5a959843d6 100644 (file)
@@ -1,3 +1,9 @@
+2002-07-18  Angus Leeming  <leeming@lyx.org>
+
+       * FileMonitor.[Ch]: new files. Monitor a file for any change and emit a
+       signal should it do so.
+
+       * Makefile.am: add FileMonitor.[Ch].
 
 2002-07-18  André Pönitz <poenitz@gmx.net>
 
diff --git a/src/support/FileMonitor.C b/src/support/FileMonitor.C
new file mode 100644 (file)
index 0000000..4bc514e
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * \file FileMonitor.C
+ * Copyright 2002 the LyX Team
+ * Read the file COPYING
+ *
+ * \author Angus Leeming <leeming@lyx.org>
+ */
+
+#include <config.h>
+
+#ifdef __GNUG__
+#pragma implementation
+#endif
+
+#include "FileMonitor.h"
+
+#include "frontends/Timeout.h"
+
+#include "support/FileInfo.h"
+#include "support/lyxlib.h"
+
+#include <boost/bind.hpp>
+#include <boost/signals/trackable.hpp>
+
+struct FileMonitor::Impl : public boost::signals::trackable {
+
+       ///
+       Impl(string const & file_with_path, int interval);
+
+       ///
+       void monitorFile();
+
+       ///
+       string filename_;
+
+       ///
+       Timeout timer_;
+
+       /// This signal is emitted if the file is modified (has a new checksum).
+       boost::signal0<void> fileChanged_;
+
+       /** We use these to ascertain whether a file (once loaded successfully)
+        *  has changed.
+        */
+       time_t timestamp_;
+       ///
+       unsigned long checksum_;
+};
+
+
+FileMonitor::FileMonitor(string const & file_with_path, int interval)
+       : pimpl_(new Impl(file_with_path, interval))
+{}
+
+
+FileMonitor::~FileMonitor()
+{}
+
+
+void FileMonitor::reset(string const & file_with_path) const
+{
+       if (pimpl_->filename_ == file_with_path)
+               return;
+
+       bool const monitor = pimpl_->timer_.running();
+       if (monitor)
+               stop();
+       
+       pimpl_->filename_ = file_with_path;
+
+       if (monitor)
+               start();
+}
+
+
+string const & FileMonitor::filename() const
+{
+       return pimpl_->filename_;
+}
+
+
+void FileMonitor::start() const
+{
+       if (monitoring())
+               return;
+
+       FileInfo finfo(pimpl_->filename_);
+       if (!finfo.isOK())
+               return;
+
+       pimpl_->timestamp_ = finfo.getModificationTime();
+       pimpl_->checksum_ = lyx::sum(pimpl_->filename_);
+
+       if (pimpl_->timestamp_ && pimpl_->checksum_) {
+               pimpl_->timer_.start();
+       } else {
+               pimpl_->timestamp_ = 0;
+               pimpl_->checksum_ = 0;
+       }
+}
+
+
+void FileMonitor::stop() const
+{
+       pimpl_->timestamp_ = 0;
+       pimpl_->checksum_ = 0;
+       pimpl_->timer_.stop();
+}
+
+
+bool FileMonitor::monitoring() const
+{
+       return pimpl_->timer_.running();
+}
+
+
+unsigned long FileMonitor::checksum() const
+{
+       // If we aren't actively monitoring the file, then recompute the
+       // checksum explicitly.
+       if (!pimpl_->timer_.running() && !pimpl_->filename_.empty())
+               return lyx::sum(pimpl_->filename_);
+
+       return pimpl_->checksum_;
+}
+
+
+boost::signals::connection FileMonitor::connect(slot_type const & slot) const
+{
+       return pimpl_->fileChanged_.connect(slot);
+}
+
+
+//------------------------------
+// Implementation details follow
+//------------------------------
+
+
+FileMonitor::Impl::Impl(string const & file_with_path, int interval)
+       : filename_(file_with_path),
+         timer_(interval, Timeout::ONETIME),
+         timestamp_(0),
+         checksum_(0)
+{
+       timer_.timeout.connect(boost::bind(&Impl::monitorFile, this));
+}
+
+
+void FileMonitor::Impl::monitorFile()
+{
+       bool changed = false;
+
+       FileInfo finfo(filename_);
+       if (!finfo.isOK()) {
+               changed = timestamp_ || checksum_;
+               timestamp_ = 0;
+               checksum_ = 0;
+
+       } else {
+               time_t const new_timestamp = finfo.getModificationTime();
+
+               if (new_timestamp != timestamp_) {
+                       timestamp_ = new_timestamp;
+
+                       unsigned long const new_checksum = lyx::sum(filename_);
+                       if (new_checksum != checksum_) {
+                               checksum_ = new_checksum;
+                               changed = true;
+                       }
+               }
+       }
+
+       timer_.start();
+       if (changed)
+               fileChanged_();
+}
diff --git a/src/support/FileMonitor.h b/src/support/FileMonitor.h
new file mode 100644 (file)
index 0000000..f91e3f2
--- /dev/null
@@ -0,0 +1,68 @@
+// -*- C++ -*-
+/*
+ * \file FileMonitor.h
+ * Copyright 2002 the LyX Team
+ * Read the file COPYING
+ *
+ * \author Angus Leeming <leeming@lyx.org>
+ *
+ * FileMonitor monitors a file and informs a listener when that file has
+ * changed.
+ */
+
+#ifndef FILEMONITOR_H
+#define FILEMONITOR_H
+
+#ifdef __GNUG__
+#pragma interface
+#endif
+
+#include "LString.h"
+
+#include <boost/utility.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/signals/signal0.hpp>
+
+class FileMonitor : boost::noncopyable {
+public:
+       /** Once monitoring begins, the file will be monitored every
+        *  interval ms.
+        */
+       FileMonitor(string const & file_with_path, int interval);
+
+       /// Define an empty d-tor out-of-line to keep boost::scoped_ptr happy.
+       ~FileMonitor();
+
+       ///
+       void reset(string const & file_with_path) const;
+
+       ///
+       string 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;
+
+       /// Connect and you'll be informed when the file has changed.
+       typedef boost::signal0<void>::slot_type slot_type;
+       ///
+       boost::signals::connection connect(slot_type const &) const;
+
+private:
+       /// Use the Pimpl idiom to hide the internals.
+       class Impl;
+
+       /// The pointer never changes although *pimpl_'s contents may.
+       boost::scoped_ptr<Impl> const pimpl_;
+};
+
+#endif // FILEMONITOR_H
index 2d03e52cfc59120c0b8a68d2a25875123000d130..0f975f6bdb751219df84de49c54c915ff6d67bce 100644 (file)
@@ -16,6 +16,8 @@ libsupport_la_SOURCES = \
        DebugStream.h \
        FileInfo.C \
        FileInfo.h \
+       FileMonitor.h \
+       FileMonitor.C \
        LAssert.C \
        LAssert.h \
        LIstream.h \