+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]
#include "GraphicsImage.h"
#include "GraphicsConverter.h"
+#include "support/FileMonitor.h"
+
#include "debug.h"
#include "support/LAssert.h"
*/
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_;
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();
}
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)
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_;
<< "\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;
/// 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,
}
+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;
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);
}
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));
}
void Loader::Impl::createPixmap()
{
- if (!cached_item_.get() || image_.get() ||
+ if (!cached_item_.get() ||
params_.display == NoDisplay || status_ != Loaded)
return;
*/
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;
+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
* ====================================================== */
/*
-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.
int old_ascent;
///
grfx::Loader loader;
+ ///
+ unsigned long checksum;
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_));
}
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.
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
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
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 "
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);
<< "\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)
+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>
--- /dev/null
+/*
+ * \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_();
+}
--- /dev/null
+// -*- 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
DebugStream.h \
FileInfo.C \
FileInfo.h \
+ FileMonitor.h \
+ FileMonitor.C \
LAssert.C \
LAssert.h \
LIstream.h \