From: Angus Leeming Date: Thu, 18 Jul 2002 14:01:42 +0000 (+0000) Subject: File monitoring is Go! X-Git-Tag: 1.6.10~18818 X-Git-Url: https://git.lyx.org/gitweb/?a=commitdiff_plain;h=6206e59009739d747b3569a21a33652b53977589;p=features.git File monitoring is Go! git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@4703 a592a061-630c-0410-9148-cb99ea01b6c8 --- diff --git a/src/graphics/ChangeLog b/src/graphics/ChangeLog index 7454502cb2..671055c217 100644 --- a/src/graphics/ChangeLog +++ b/src/graphics/ChangeLog @@ -1,3 +1,15 @@ +2002-07-18 Angus Leeming + + * 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 * Makefile.am: remove FileMonitor.[Ch] diff --git a/src/graphics/GraphicsCacheItem.C b/src/graphics/GraphicsCacheItem.C index 44dd44c867..95092e42d1 100644 --- a/src/graphics/GraphicsCacheItem.C +++ b/src/graphics/GraphicsCacheItem.C @@ -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; diff --git a/src/graphics/GraphicsCacheItem.h b/src/graphics/GraphicsCacheItem.h index 15f441a4c0..bc80bd282d 100644 --- a/src/graphics/GraphicsCacheItem.h +++ b/src/graphics/GraphicsCacheItem.h @@ -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, diff --git a/src/graphics/GraphicsLoader.C b/src/graphics/GraphicsLoader.C index 6bbd507bef..c819f4379f 100644 --- a/src/graphics/GraphicsLoader.C +++ b/src/graphics/GraphicsLoader.C @@ -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; diff --git a/src/graphics/GraphicsLoader.h b/src/graphics/GraphicsLoader.h index f0959daf73..9ac9e45888 100644 --- a/src/graphics/GraphicsLoader.h +++ b/src/graphics/GraphicsLoader.h @@ -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; diff --git a/src/insets/ChangeLog b/src/insets/ChangeLog index baf2e83933..f0f953a2de 100644 --- a/src/insets/ChangeLog +++ b/src/insets/ChangeLog @@ -1,3 +1,10 @@ +2002-07-18 Angus Leeming + + * 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 * insetgraphics.C (Cache c-tor): bind to the GraphicsLoader through diff --git a/src/insets/insetgraphics.C b/src/insets/insetgraphics.C index 7cad1907e9..424fef4eb2 100644 --- a/src/insets/insetgraphics.C +++ b/src/insets/insetgraphics.C @@ -10,25 +10,7 @@ * ====================================================== */ /* -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) diff --git a/src/stamp-h.in b/src/stamp-h.in index 9788f70238..e69de29bb2 100644 --- a/src/stamp-h.in +++ b/src/stamp-h.in @@ -1 +0,0 @@ -timestamp diff --git a/src/support/ChangeLog b/src/support/ChangeLog index 360d90e932..69d62acebf 100644 --- a/src/support/ChangeLog +++ b/src/support/ChangeLog @@ -1,3 +1,9 @@ +2002-07-18 Angus Leeming + + * 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 diff --git a/src/support/FileMonitor.C b/src/support/FileMonitor.C new file mode 100644 index 0000000000..4bc514e581 --- /dev/null +++ b/src/support/FileMonitor.C @@ -0,0 +1,176 @@ +/* + * \file FileMonitor.C + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author Angus Leeming + */ + +#include + +#ifdef __GNUG__ +#pragma implementation +#endif + +#include "FileMonitor.h" + +#include "frontends/Timeout.h" + +#include "support/FileInfo.h" +#include "support/lyxlib.h" + +#include +#include + +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 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 index 0000000000..f91e3f2541 --- /dev/null +++ b/src/support/FileMonitor.h @@ -0,0 +1,68 @@ +// -*- C++ -*- +/* + * \file FileMonitor.h + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author Angus Leeming + * + * 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 +#include +#include + +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::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 const pimpl_; +}; + +#endif // FILEMONITOR_H diff --git a/src/support/Makefile.am b/src/support/Makefile.am index 2d03e52cfc..0f975f6bdb 100644 --- a/src/support/Makefile.am +++ b/src/support/Makefile.am @@ -16,6 +16,8 @@ libsupport_la_SOURCES = \ DebugStream.h \ FileInfo.C \ FileInfo.h \ + FileMonitor.h \ + FileMonitor.C \ LAssert.C \ LAssert.h \ LIstream.h \