2 * \file GraphicsCacheItem.C
3 * Copyright 2002 the LyX Team
4 * Read the file COPYING
6 * \author Baruch Even <baruch.even@writeme.com>
7 * \author Herbert Voss <voss@lyx.org>
8 * \author Angus Leeming <a.leeming@ic.ac.uk>
14 #pragma implementation
17 #include "graphics/GraphicsCacheItem.h"
18 #include "graphics/GraphicsImage.h"
19 #include "graphics/GraphicsConverter.h"
23 #include "support/LAssert.h"
24 #include "support/filetools.h"
26 #include <boost/shared_ptr.hpp>
27 #include <boost/bind.hpp>
28 #include <boost/signals/connection.hpp>
29 #include <boost/signals/trackable.hpp>
35 struct CacheItem::Impl : public boost::signals::trackable {
38 Impl(CacheItem &, string const & file);
40 /** Start the image conversion process, checking first that it is
41 * necessary. If it is necessary, then a conversion task is started.
42 * CacheItem asumes that the conversion is asynchronous and so
43 * passes a Signal to the converting routine. When the conversion
44 * is finished, this Signal is emitted, returning the converted
45 * file to this->imageConverted.
47 * If no file conversion is needed, then convertToDisplayFormat() calls
48 * loadImage() directly.
50 * convertToDisplayFormat() will set the loading status flag as
51 * approriate through calls to setStatus().
53 void convertToDisplayFormat();
55 /** Load the image into memory. This is called either from
56 * convertToDisplayFormat() direct or from imageConverted().
60 /** Get a notification when the image conversion is done.
61 * Connected to a signal on_finish_ which is passed to
64 void imageConverted(bool);
66 /** Get a notification when the image loading is done.
67 * Connected to a signal on_finish_ which is passed to
68 * grfx::Image::loadImage.
70 void imageLoaded(bool);
72 /** Sets the status of the loading process. Also notifies
73 * listeners that the status has chacnged.
75 void setStatus(ImageStatus new_status);
80 /// The filename we refer too.
82 /// Is the file compressed?
84 /// If so, store the uncompressed file in this temporary file.
85 string unzipped_filename_;
86 /// What file are we trying to load?
88 /** Should we delete the file after loading? True if the file is
89 * the result of a conversion process.
91 bool remove_loaded_file_;
93 /// The image and its loading status.
94 boost::shared_ptr<Image> image_;
98 /// The connection to the signal Image::finishedLoading
99 boost::signals::connection cl_;
101 /// The connection of the signal ConvProcess::finishedConversion,
102 boost::signals::connection cc_;
105 boost::scoped_ptr<Converter> converter_;
109 CacheItem::CacheItem(string const & file)
110 : pimpl_(new Impl(*this, file))
114 CacheItem::~CacheItem()
118 string const & CacheItem::filename() const
120 return pimpl_->filename_;
124 void CacheItem::startLoading()
126 if (pimpl_->status_ != WaitingToLoad)
129 pimpl_->convertToDisplayFormat();
133 Image const * CacheItem::image() const
135 return pimpl_->image_.get();
139 ImageStatus CacheItem::status() const
141 return pimpl_->status_;
145 //------------------------------
146 // Implementation details follow
147 //------------------------------
150 CacheItem::Impl::Impl(CacheItem & p, string const & file)
151 : parent_(p), filename_(file), zipped_(false),
152 remove_loaded_file_(false), status_(WaitingToLoad)
156 void CacheItem::Impl::setStatus(ImageStatus new_status)
158 if (status_ == new_status)
161 status_ = new_status;
162 parent_.statusChanged();
166 void CacheItem::Impl::imageConverted(bool success)
168 string const text = success ? "succeeded" : "failed";
169 lyxerr[Debug::GRAPHICS] << "Image conversion " << text << "." << endl;
171 file_to_load_ = converter_.get() ?
172 converter_->convertedFile() : string();
176 success = !file_to_load_.empty() && IsFileReadable(file_to_load_);
177 lyxerr[Debug::GRAPHICS] << "Unable to find converted file!" << endl;
180 setStatus(ErrorConverting);
183 lyx::unlink(unzipped_filename_);
192 // This function gets called from the callback after the image has been
193 // converted successfully.
194 void CacheItem::Impl::loadImage()
197 lyxerr[Debug::GRAPHICS] << "Loading image." << endl;
199 image_ = Image::newImage();
202 cl_ = image_->finishedLoading.connect(
203 boost::bind(&Impl::imageLoaded, this, _1));
204 image_->load(file_to_load_);
208 void CacheItem::Impl::imageLoaded(bool success)
210 string const text = success ? "succeeded" : "failed";
211 lyxerr[Debug::GRAPHICS] << "Image loading " << text << "." << endl;
213 // Clean up after loading.
215 lyx::unlink(unzipped_filename_);
217 if (remove_loaded_file_ && unzipped_filename_ != file_to_load_)
218 lyx::unlink(file_to_load_);
223 setStatus(ErrorLoading);
235 string const findTargetFormat(string const & from)
237 typedef grfx::Image::FormatList FormatList;
238 FormatList const formats = grfx::Image::loadableFormats();
240 // There must be a format to load from.
241 lyx::Assert(!formats.empty());
243 // First ascertain if we can load directly with no conversion
244 FormatList::const_iterator it1 = formats.begin();
245 FormatList::const_iterator end = formats.end();
246 for (; it1 != end; ++it1) {
251 // So, we have to convert to a loadable format. Can we?
252 FormatList::const_iterator it2 = formats.begin();
253 for (; it2 != end; ++it2) {
254 if (grfx::Converter::isReachable(from, *it2))
258 // Failed! so we have to try to convert it to XPM format
259 // with the standard converter
260 return string("xpm");
268 void CacheItem::Impl::convertToDisplayFormat()
270 setStatus(Converting);
271 // Make a local copy in case we unzip it
272 string const filename = zippedFile(filename_) ?
273 unzipFile(filename_) : filename_;
274 string const displayed_filename = MakeDisplayPath(filename_);
275 lyxerr[Debug::GRAPHICS] << "[GrahicsCacheItem::convertToDisplayFormat]\n"
276 << "\tAttempting to convert image file: " << filename
277 << "\n\twith displayed filename: " << displayed_filename
280 // First, check that the file exists!
281 if (!IsFileReadable(filename)) {
282 setStatus(ErrorNoFile);
283 lyxerr[Debug::GRAPHICS] << "\tThe file is not readable" << endl;
287 string from = getExtFromContents(filename);
288 // Some old ps-files make problems, so we do not need direct
289 // loading of an ps-file
291 lyxerr[Debug::GRAPHICS]
292 << "\n\tThe file contains PostScript format data.\n"
293 << "\tchanging it to eps-format to get it converted to xpm\n";
296 lyxerr[Debug::GRAPHICS]
297 << "\n\tThe file contains " << from << " format data." << endl;
298 string const to = findTargetFormat(from);
301 // No conversion needed!
302 lyxerr[Debug::GRAPHICS] << "\tNo conversion needed (from == to)!" << endl;
303 file_to_load_ = filename;
308 lyxerr[Debug::GRAPHICS] << "\tConverting it to " << to << " format." << endl;
309 // Take only the filename part of the file, without path or extension.
310 string const temp = ChangeExtension(OnlyFilename(filename), string());
312 // Add some stuff to create a uniquely named temporary file.
313 // This file is deleted in loadImage after it is loaded into memory.
314 string const to_file_base = lyx::tempName(string(), temp);
315 remove_loaded_file_ = true;
317 // Remove the temp file, we only want the name...
318 lyx::unlink(to_file_base);
320 // Connect a signal to this->imageConverted and pass this signal to
321 // the graphics converter so that we can load the modified file
322 // on completion of the conversion process.
323 converter_.reset(new Converter(filename, to_file_base, from, to));
324 converter_->finishedConversion.connect(
325 boost::bind(&Impl::imageConverted, this, _1));
326 converter_->startConversion();