File monitoring is Go!

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@4703 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Angus Leeming 2002-07-18 14:01:42 +00:00
parent 10fd14e55b
commit 6206e59009
12 changed files with 478 additions and 83 deletions

View 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> 2002-07-17 Jean-Marc Lasgouttes <lasgouttes@freesurf.fr>
* Makefile.am: remove FileMonitor.[Ch] * Makefile.am: remove FileMonitor.[Ch]

View File

@ -18,6 +18,8 @@
#include "GraphicsImage.h" #include "GraphicsImage.h"
#include "GraphicsConverter.h" #include "GraphicsConverter.h"
#include "support/FileMonitor.h"
#include "debug.h" #include "debug.h"
#include "support/LAssert.h" #include "support/LAssert.h"
@ -73,8 +75,22 @@ struct CacheItem::Impl : public boost::signals::trackable {
*/ */
void setStatus(ImageStatus new_status); 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. /// The filename we refer too.
string const filename_; string const filename_;
///
FileMonitor const monitor_;
/// Is the file compressed? /// Is the file compressed?
bool zipped_; bool zipped_;
@ -123,10 +139,26 @@ string const & CacheItem::filename() const
void CacheItem::startLoading() const void CacheItem::startLoading() const
{ {
if (pimpl_->status_ != WaitingToLoad) pimpl_->startLoading();
return; }
pimpl_->convertToDisplayFormat();
void CacheItem::startMonitoring() const
{
if (!pimpl_->monitor_.monitoring())
pimpl_->monitor_.start();
}
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) CacheItem::Impl::Impl(string const & file)
: filename_(file), zipped_(false), : filename_(file),
remove_loaded_file_(false), status_(WaitingToLoad) 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::setStatus(ImageStatus new_status)
@ -276,6 +350,17 @@ namespace grfx {
void CacheItem::Impl::convertToDisplayFormat() void CacheItem::Impl::convertToDisplayFormat()
{ {
setStatus(Converting); 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 // Make a local copy in case we unzip it
string const filename = zippedFile(filename_) ? string const filename = zippedFile(filename_) ?
unzipFile(filename_) : filename_; unzipFile(filename_) : filename_;
@ -285,13 +370,6 @@ void CacheItem::Impl::convertToDisplayFormat()
<< "\n\twith displayed filename: " << displayed_filename << "\n\twith displayed filename: " << displayed_filename
<< endl; << 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); string from = getExtFromContents(filename);
lyxerr[Debug::GRAPHICS] lyxerr[Debug::GRAPHICS]
<< "\n\tThe file contains " << from << " format data." << endl; << "\n\tThe file contains " << from << " format data." << endl;

View File

@ -59,6 +59,19 @@ public:
/// It's in the cache. Now start the loading process. /// It's in the cache. Now start the loading process.
void startLoading() const; 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(). /** Get the image associated with filename().
* If the image is not yet loaded, returns 0. * If the image is not yet loaded, returns 0.
* This routine returns a pointer to const; if you want to modify it, * This routine returns a pointer to const; if you want to modify it,

View 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 string const & Loader::filename() const
{ {
static string const empty; static string const empty;
@ -187,7 +214,12 @@ void Loader::Impl::resetFile(string const & file)
if (file == old_file) if (file == old_file)
return; return;
// If monitoring() the current file, should continue to monitor the
// new file.
bool continue_monitoring = false;
if (!old_file.empty()) { if (!old_file.empty()) {
continue_monitoring = cached_item_->monitoring();
cached_item_.reset(); cached_item_.reset();
Cache::get().remove(old_file); Cache::get().remove(old_file);
} }
@ -206,6 +238,9 @@ void Loader::Impl::resetFile(string const & file)
cached_item_ = gc.item(file); cached_item_ = gc.item(file);
status_ = cached_item_->status(); status_ = cached_item_->status();
if (continue_monitoring && !cached_item_->monitoring())
cached_item_->startMonitoring();
cached_item_->connect(boost::bind(&Impl::statusChanged, this)); cached_item_->connect(boost::bind(&Impl::statusChanged, this));
} }
@ -231,7 +266,7 @@ void Loader::Impl::statusChanged()
void Loader::Impl::createPixmap() void Loader::Impl::createPixmap()
{ {
if (!cached_item_.get() || image_.get() || if (!cached_item_.get() ||
params_.display == NoDisplay || status_ != Loaded) params_.display == NoDisplay || status_ != Loaded)
return; return;

View File

@ -73,6 +73,19 @@ public:
*/ */
void startLoading(Inset const &, BufferView const &) const; 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? /// How far have we got in loading the image?
ImageStatus status() const; ImageStatus status() const;

View 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> 2002-07-17 Angus Leeming <leeming@lyx.org>
* insetgraphics.C (Cache c-tor): bind to the GraphicsLoader through * insetgraphics.C (Cache c-tor): bind to the GraphicsLoader through

View File

@ -10,25 +10,7 @@
* ====================================================== */ * ====================================================== */
/* /*
Known BUGS: TODO
* 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:
* What advanced features the users want to do? * What advanced features the users want to do?
Implement them in a non latex dependent way, but a logical way. 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; int old_ascent;
/// ///
grfx::Loader loader; grfx::Loader loader;
///
unsigned long checksum;
private: private:
/// ///
@ -170,7 +154,7 @@ private:
InsetGraphics::Cache::Cache(InsetGraphics & p) InsetGraphics::Cache::Cache(InsetGraphics & p)
: old_ascent(0), parent_(p) : old_ascent(0), checksum(0), parent_(p)
{ {
loader.connect(boost::bind(&InsetGraphics::statusChanged, &parent_)); loader.connect(boost::bind(&InsetGraphics::statusChanged, &parent_));
} }
@ -327,9 +311,11 @@ void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
int old_x = int(x); int old_x = int(x);
x += lwidth; x += lwidth;
if (cache_->loader.status() == grfx::WaitingToLoad) { if (cache_->loader.status() == grfx::WaitingToLoad)
cache_->loader.startLoading(*this, *bv); 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, // This will draw the graphics. If the graphics has not been loaded yet,
// we draw just a rectangle. // we draw just a rectangle.
@ -626,41 +612,39 @@ string const InsetGraphics::prepareFile(Buffer const *buf) const
if (!IsFileReadable(orig_file_with_path)) if (!IsFileReadable(orig_file_with_path))
return orig_file; 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); bool const zipped = zippedFile(orig_file_with_path);
if (zipped)
lyxerr[Debug::GRAPHICS] << "\twe have a zipped file (" // If the file is compressed and we have specified that it
<< getExtFromContents(orig_file_with_path) << ")\n"; // should not be uncompressed, then just return its name and
if (params().noUnzip && zipped) { // let LaTeX do the rest!
if (zipped && params().noUnzip) {
lyxerr[Debug::GRAPHICS] lyxerr[Debug::GRAPHICS]
<< "\tpass file unzipped to LaTeX but with full path.\n"; << "\tpass zipped file to LaTeX but with full path.\n";
// latex needs an absolue path, otherwise the coresponding // LaTeX needs an absolue path, otherwise the
// *.eps.bb file isn't found // coresponding *.eps.bb file isn't found
return orig_file_with_path; 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); 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) { 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); temp_file = MakeAbsPath(OnlyFilename(temp_file), buf->tmppath);
lyxerr[Debug::GRAPHICS] lyxerr[Debug::GRAPHICS]
<< "\ttemp_file: " << temp_file << endl; << "\ttemp_file: " << temp_file << endl;
if (!IsFileReadable(temp_file)) { if (file_has_changed || !IsFileReadable(temp_file)) {
bool const success = lyx::copy(orig_file_with_path, temp_file); bool const success = lyx::copy(orig_file_with_path,
temp_file);
lyxerr[Debug::GRAPHICS] lyxerr[Debug::GRAPHICS]
<< "\tCopying zipped file from " << "\tCopying zipped file from "
<< orig_file_with_path << " to " << temp_file << orig_file_with_path << " to " << temp_file
@ -673,16 +657,17 @@ string const InsetGraphics::prepareFile(Buffer const *buf) const
lyxerr[Debug::GRAPHICS] lyxerr[Debug::GRAPHICS]
<< "\tunzipped to " << orig_file_with_path << endl; << "\tunzipped to " << orig_file_with_path << endl;
} }
string const from = getExtFromContents(orig_file_with_path);
// "nice" means that the buffer is exported to LaTeX format but not string const from = getExtFromContents(orig_file_with_path);
// run through the LaTeX compiler. string const to = findTargetFormat(from);
// if (nice) lyxerr[Debug::GRAPHICS]
// no conversion needed! << "\t we have: from " << from << " to " << to << '\n';
// Return the original filename as is, because we do not know
// what the user decide. if (from == to && !lyxrc.use_tempdir)
if (buf->niceFile) // No conversion is needed. LaTeX can handle the
return orig_file; // 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 // We're going to be running the exported buffer through the LaTeX
// compiler, so must ensure that LaTeX can cope with the graphics // 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] lyxerr[Debug::GRAPHICS]
<< "\tchanged to: " << temp_file << endl; << "\tchanged to: " << temp_file << endl;
// if the file doen't exists, copy it into the tempdi // if the file doen't exists, copy it into the tempdir
if (!IsFileReadable(temp_file)) { if (file_has_changed || !IsFileReadable(temp_file)) {
bool const success = lyx::copy(orig_file_with_path, temp_file); bool const success = lyx::copy(orig_file_with_path, temp_file);
lyxerr[Debug::GRAPHICS] lyxerr[Debug::GRAPHICS]
<< "\tcopying from " << orig_file_with_path << " to " << "\tcopying from " << orig_file_with_path << " to "
@ -724,18 +709,11 @@ string const InsetGraphics::prepareFile(Buffer const *buf) const
return orig_file; return orig_file;
} }
} }
}
string const to = findTargetFormat(from); if (from == to)
lyxerr[Debug::GRAPHICS] // No conversion is needed. LaTeX can handle the
<< "\t we have: from " << from << " to " << to << '\n'; // graphic file as is.
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)
return RemoveExtension(temp_file); return RemoveExtension(temp_file);
return RemoveExtension(orig_file_with_path);
} }
string const outfile_base = RemoveExtension(temp_file); string const outfile_base = RemoveExtension(temp_file);
@ -821,6 +799,14 @@ int InsetGraphics::latex(Buffer const *buf, ostream & os,
<< "\tBefore = " << before << "\tBefore = " << before
<< "\n\tafter = " << after << endl; << "\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 // Make the filename relative to the lyx file
// and remove the extension so the LaTeX will use whatever is // and remove the extension so the LaTeX will use whatever is
// appropriate (when there are several versions in different formats) // appropriate (when there are several versions in different formats)

View File

@ -1 +0,0 @@
timestamp

View 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> 2002-07-18 André Pönitz <poenitz@gmx.net>

176
src/support/FileMonitor.C Normal file
View File

@ -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_();
}

68
src/support/FileMonitor.h Normal file
View File

@ -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

View File

@ -16,6 +16,8 @@ libsupport_la_SOURCES = \
DebugStream.h \ DebugStream.h \
FileInfo.C \ FileInfo.C \
FileInfo.h \ FileInfo.h \
FileMonitor.h \
FileMonitor.C \
LAssert.C \ LAssert.C \
LAssert.h \ LAssert.h \
LIstream.h \ LIstream.h \