X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fgraphics%2FGraphicsCacheItem.C;h=59973327d441815b6bbf1ff202eb94fc5f7c8e6c;hb=33243f70037b067f90d1574b74b34f90a2ef2aa1;hp=a6ebdcc1cad1bbf750976541dcf7cf60313d2384;hpb=7ea7dabed1b72cc25dcbdc482ac006f2b61dacfd;p=lyx.git diff --git a/src/graphics/GraphicsCacheItem.C b/src/graphics/GraphicsCacheItem.C index a6ebdcc1ca..59973327d4 100644 --- a/src/graphics/GraphicsCacheItem.C +++ b/src/graphics/GraphicsCacheItem.C @@ -1,206 +1,439 @@ -/* This file is part of - * ================================================= - * - * LyX, The Document Processor - * Copyright 1995 Matthias Ettrich. - * Copyright 1995-2001 The LyX Team. +/** + * \file GraphicsCacheItem.C + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. * * \author Baruch Even - * \author Herbert Voss - * ================================================= */ + * \author Herbert Voß + * \author Angus Leeming + * + * Full author contact details are available in file CREDITS. + */ #include -#ifdef __GNUG__ -#pragma implementation -#endif +#include "GraphicsCacheItem.h" +#include "GraphicsConverter.h" +#include "GraphicsImage.h" -#include "graphics/GraphicsCacheItem.h" -#include "graphics/GraphicsCache.h" -#include "graphics/ImageLoaderXPM.h" -#include "converter.h" -#include "lyx_gui_misc.h" #include "debug.h" -#include "support/LAssert.h" -#include "gettext.h" -#include "lyxfunc.h" - -#include "frontends/support/LyXImage.h" #include "support/filetools.h" +#include "support/FileMonitor.h" #include "support/lyxlib.h" +#include + + +namespace support = lyx::support; + +using support::ChangeExtension; +using support::FileMonitor; +using support::IsFileReadable; +using support::MakeDisplayPath; +using support::OnlyFilename; +using support::getExtFromContents; +using support::tempName; +using support::unlink; +using support::unzipFile; +using support::unzippedFileName; +using support::zippedFile; + using std::endl; +using std::string; + + +namespace lyx { +namespace graphics { + +struct CacheItem::Impl : public boost::signals::trackable { + + /// + Impl(string const & file); + + /** Start the image conversion process, checking first that it is + * necessary. If it is necessary, then a conversion task is started. + * CacheItem asumes that the conversion is asynchronous and so + * passes a Signal to the converting routine. When the conversion + * is finished, this Signal is emitted, returning the converted + * file to this->imageConverted. + * + * If no file conversion is needed, then convertToDisplayFormat() calls + * loadImage() directly. + * + * convertToDisplayFormat() will set the loading status flag as + * approriate through calls to setStatus(). + */ + void convertToDisplayFormat(); + + /** Load the image into memory. This is called either from + * convertToDisplayFormat() direct or from imageConverted(). + */ + void loadImage(); + + /** Get a notification when the image conversion is done. + * Connected to a signal on_finish_ which is passed to + * Converter::convert. + */ + void imageConverted(bool); + + /** Get a notification when the image loading is done. + * Connected to a signal on_finish_ which is passed to + * lyx::graphics::Image::loadImage. + */ + void imageLoaded(bool); + + /** Sets the status of the loading process. Also notifies + * listeners that the status has changed. + */ + 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_; + /// If so, store the uncompressed file in this temporary file. + string unzipped_filename_; + /// What file are we trying to load? + string file_to_load_; + /** Should we delete the file after loading? True if the file is + * the result of a conversion process. + */ + bool remove_loaded_file_; + + /// The image and its loading status. + boost::shared_ptr image_; + /// + ImageStatus status_; + + /// This signal is emitted when the image loading status changes. + boost::signal0 statusChanged; + + /// The connection to the signal Image::finishedLoading + boost::signals::connection cl_; + + /// The connection of the signal ConvProcess::finishedConversion, + boost::signals::connection cc_; + + /// + boost::scoped_ptr converter_; +}; + + +CacheItem::CacheItem(string const & file) + : pimpl_(new Impl(file)) +{} -/* - * The order of conversion: - * - * The c-tor calls convertImage() - * - * convertImage() verifies that we need to do conversion, if not it will just - * call the loadImage() - * if conversion is needed, it will initiate the conversion. - * - * When the conversion is completed imageConverted() is called, which in turn - * calls loadImage(). - * - * Since we currently do everything synchronously, convertImage() calls - * imageConverted() right after it does the call to the conversion process. -*/ +CacheItem::~CacheItem() +{} + -GraphicsCacheItem::GraphicsCacheItem(string const & filename) - : filename_(filename), imageStatus_(GraphicsCacheItem::Loading) +string const & CacheItem::filename() const { - bool success = convertImage(filename); - if (!success) // Conversion failed miserably (couldn't even start). - setStatus(ErrorConverting); + return pimpl_->filename_; } -GraphicsCacheItem::~GraphicsCacheItem() -{} +void CacheItem::startLoading() const +{ + pimpl_->startLoading(); +} + + +void CacheItem::startMonitoring() const +{ + if (!pimpl_->monitor_.monitoring()) + pimpl_->monitor_.start(); +} -GraphicsCacheItem::ImageStatus -GraphicsCacheItem::getImageStatus() const +bool CacheItem::monitoring() const { - return imageStatus_; + return pimpl_->monitor_.monitoring(); } -void GraphicsCacheItem::setStatus(ImageStatus new_status) +unsigned long CacheItem::checksum() const { - imageStatus_ = new_status; + return pimpl_->monitor_.checksum(); } -LyXImage * -GraphicsCacheItem::getImage() const +Image const * CacheItem::image() const { - return image_.get(); + return pimpl_->image_.get(); } -void GraphicsCacheItem::imageConverted(bool success) +ImageStatus CacheItem::status() const { - // Debug output - string text = "succeeded"; - if (!success) - text = "failed"; - lyxerr << "imageConverted, conversion " << text << "." << endl; + return pimpl_->status_; +} + + +boost::signals::connection CacheItem::connect(slot_type const & slot) const +{ + return pimpl_->statusChanged.connect(slot); +} + + +//------------------------------ +// Implementation details follow +//------------------------------ + + +CacheItem::Impl::Impl(string const & file) + : 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()) + unlink(unzipped_filename_); + unzipped_filename_.erase(); + + if (remove_loaded_file_ && !file_to_load_.empty()) + unlink(file_to_load_); + remove_loaded_file_ = false; + file_to_load_.erase(); + + 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) +{ + if (status_ == new_status) + return; + + status_ = new_status; + statusChanged(); +} + + +void CacheItem::Impl::imageConverted(bool success) +{ + string const text = success ? "succeeded" : "failed"; + lyxerr[Debug::GRAPHICS] << "Image conversion " << text << '.' << endl; + + file_to_load_ = converter_.get() ? + converter_->convertedFile() : string(); + converter_.reset(); + cc_.disconnect(); + + success = !file_to_load_.empty() && IsFileReadable(file_to_load_); + + if (!success) { + lyxerr[Debug::GRAPHICS] << "Unable to find converted file!" + << endl; + setStatus(ErrorConverting); + + if (zipped_) + unlink(unzipped_filename_); - if (! success) { - lyxerr << "(GraphicsCacheItem::imageConverter) " - "Error converting image." << endl; - setStatus(GraphicsCacheItem::ErrorConverting); return; } - // Do the actual image loading from file to memory. - loadImage(); + loadImage(); } -namespace { +// This function gets called from the callback after the image has been +// converted successfully. +void CacheItem::Impl::loadImage() +{ + setStatus(Loading); + lyxerr[Debug::GRAPHICS] << "Loading image." << endl; -string const findTargetFormat(string const & from) + image_ = Image::newImage(); + + cl_.disconnect(); + cl_ = image_->finishedLoading.connect( + boost::bind(&Impl::imageLoaded, this, _1)); + image_->load(file_to_load_); +} + + +void CacheItem::Impl::imageLoaded(bool success) { - typedef ImageLoader::FormatList FormatList; - FormatList formats = ImageLoaderXPM().loadableFormats(); - lyx::Assert(formats.size() > 0); // There must be a format to load from. + string const text = success ? "succeeded" : "failed"; + lyxerr[Debug::GRAPHICS] << "Image loading " << text << '.' << endl; + + // Clean up after loading. + if (zipped_) + unlink(unzipped_filename_); + + if (remove_loaded_file_ && unzipped_filename_ != file_to_load_) + unlink(file_to_load_); + + cl_.disconnect(); + + if (!success) { + setStatus(ErrorLoading); + return; + } + + // Inform the outside world. + setStatus(Loaded); +} + - FormatList::const_iterator iter = formats.begin(); - FormatList::const_iterator end = formats.end(); +} // namespace graphics +} // namespace lyx - for (; iter != end; ++iter) { - if (converters.isReachable(from, *iter)) - break; + +namespace { + +string const findTargetFormat(string const & from) +{ + typedef lyx::graphics::Image::FormatList FormatList; + FormatList const formats = lyx::graphics::Image::loadableFormats(); + + // There must be a format to load from. + BOOST_ASSERT(!formats.empty()); + + // First ascertain if we can load directly with no conversion + FormatList::const_iterator it = formats.begin(); + FormatList::const_iterator end = formats.end(); + for (; it != end; ++it) { + if (from == *it) + return *it; } - if (iter == end) { - // We do not know how to convert the image to something loadable. - lyxerr << "ERROR: Do not know how to convert image." << endl; - return string(); + + // So, we have to convert to a loadable format. Can we? + it = formats.begin(); + for (; it != end; ++it) { + if (lyx::graphics::Converter::isReachable(from, *it)) + return *it; + else + lyxerr[Debug::GRAPHICS] + << "Unable to convert from " << from + << " to " << *it << std::endl; } - return (*iter); + + // Failed! so we have to try to convert it to PPM format + // with the standard converter + return string("ppm"); } } // anon namespace - -bool GraphicsCacheItem::convertImage(string const & filename) -{ - setStatus(GraphicsCacheItem::Converting); -#warning shadowing class variable (Lgb) - // Is this needed at all? - string filename_ = string(filename); - lyxerr << "try to convert image file: " << filename_ << endl; -// 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-------------*/ - - lyxerr << "GetExtension: " << GetExtension(filename_) << endl; - bool const zipped = zippedFile(filename_); - if (zipped) - filename_ = unzipFile(filename_); - string const from = getExtFromContents(filename_); // get the type - lyxerr << "GetExtFromContents: " << from << endl; + +namespace lyx { +namespace graphics { + +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 filename; + if ((zipped_ = zippedFile(filename_))) { + unzipped_filename_ = tempName(string(), filename_); + if (unzipped_filename_.empty()) { + setStatus(ErrorConverting); + lyxerr[Debug::GRAPHICS] + << "\tCould not create temporary file." << endl; + return; + } + filename = unzipFile(filename_, unzipped_filename_); + } else + filename = filename_; + + string const displayed_filename = MakeDisplayPath(filename_); + lyxerr[Debug::GRAPHICS] << "[GrahicsCacheItem::convertToDisplayFormat]\n" + << "\tAttempting to convert image file: " << filename + << "\n\twith displayed filename: " << displayed_filename + << endl; + + string from = getExtFromContents(filename); + lyxerr[Debug::GRAPHICS] + << "\n\tThe file contains " << from << " format data." << endl; string const to = findTargetFormat(from); - lyxerr << "from: " << from << " -> " << to << endl; - if (to.empty()) - return false; - // manage zipped files. unzip them first into the tempdir + if (from == to) { // No conversion needed! - // Saves more than just time: prevents the deletion of - // the "to" file after loading when it's the same as the "from"! - tempfile = filename_; - loadImage(); - return true; + lyxerr[Debug::GRAPHICS] << "\tNo conversion needed (from == to)!" << endl; + file_to_load_ = filename; + loadImage(); + return; } + + lyxerr[Debug::GRAPHICS] << "\tConverting it to " << to << " format." << endl; // Take only the filename part of the file, without path or extension. - string temp = OnlyFilename(filename_); - temp = ChangeExtension(filename_, string()); - - // Add some stuff to have it a unique temp file. - // This tempfile is deleted in loadImage after it is loaded to memory. - tempfile = lyx::tempName(string(), temp); - // Remove the temp file, we only want the name... - lyx::unlink(tempfile); - bool result = converters.convert(0, filename_, tempfile, from, to); - tempfile.append(".xpm"); - // For now we are synchronous - imageConverted(result); - // Cleanup after the conversion. - lyx::unlink(tempfile); - if (zipped) - lyx::unlink(filename_); - tempfile.erase(); - return true; -} + string const temp = ChangeExtension(OnlyFilename(filename), string()); + // Add some stuff to create a uniquely named temporary file. + // This file is deleted in loadImage after it is loaded into memory. + string const to_file_base = tempName(string(), temp); + remove_loaded_file_ = true; -// This function gets called from the callback after the image has been -// converted successfully. -void -GraphicsCacheItem::loadImage() -{ - lyxerr << "Loading XPM Image... "; - - ImageLoaderXPM imageLoader; - if (imageLoader.loadImage(tempfile) == ImageLoader::OK) { - lyxerr << "Success." << endl; - image_.reset(imageLoader.getImage()); - setStatus(GraphicsCacheItem::Loaded); - } else { - lyxerr << "Loading " << tempfile << "Failed" << endl; - setStatus(GraphicsCacheItem::ErrorReading); - } + // Remove the temp file, we only want the name... + // FIXME: This is unsafe! + unlink(to_file_base); + + // Connect a signal to this->imageConverted and pass this signal to + // the graphics converter so that we can load the modified file + // on completion of the conversion process. + converter_.reset(new Converter(filename, to_file_base, from, to)); + converter_->connect(boost::bind(&Impl::imageConverted, this, _1)); + converter_->startConversion(); } + +} // namespace graphics +} // namespace lyx