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 <leeming@lyx.org>
14 #pragma implementation
17 #include "GraphicsCacheItem.h"
18 #include "GraphicsImage.h"
19 #include "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/trackable.hpp>
34 struct CacheItem::Impl : public boost::signals::trackable {
37 Impl(string const & file);
39 /** Start the image conversion process, checking first that it is
40 * necessary. If it is necessary, then a conversion task is started.
41 * CacheItem asumes that the conversion is asynchronous and so
42 * passes a Signal to the converting routine. When the conversion
43 * is finished, this Signal is emitted, returning the converted
44 * file to this->imageConverted.
46 * If no file conversion is needed, then convertToDisplayFormat() calls
47 * loadImage() directly.
49 * convertToDisplayFormat() will set the loading status flag as
50 * approriate through calls to setStatus().
52 void convertToDisplayFormat();
54 /** Load the image into memory. This is called either from
55 * convertToDisplayFormat() direct or from imageConverted().
59 /** Get a notification when the image conversion is done.
60 * Connected to a signal on_finish_ which is passed to
63 void imageConverted(bool);
65 /** Get a notification when the image loading is done.
66 * Connected to a signal on_finish_ which is passed to
67 * grfx::Image::loadImage.
69 void imageLoaded(bool);
71 /** Sets the status of the loading process. Also notifies
72 * listeners that the status has changed.
74 void setStatus(ImageStatus new_status);
76 /// The filename we refer too.
77 string const filename_;
79 /// Is the file compressed?
81 /// If so, store the uncompressed file in this temporary file.
82 string unzipped_filename_;
83 /// What file are we trying to load?
85 /** Should we delete the file after loading? True if the file is
86 * the result of a conversion process.
88 bool remove_loaded_file_;
90 /// The image and its loading status.
91 boost::shared_ptr<Image> image_;
95 /// This signal is emitted when the image loading status changes.
96 boost::signal0<void> statusChanged;
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(file))
114 CacheItem::~CacheItem()
118 string const & CacheItem::filename() const
120 return pimpl_->filename_;
124 void CacheItem::startLoading() const
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 boost::signals::connection CacheItem::connect(slot_type const & slot) const
147 return pimpl_->statusChanged.connect(slot);
151 //------------------------------
152 // Implementation details follow
153 //------------------------------
156 CacheItem::Impl::Impl(string const & file)
157 : filename_(file), zipped_(false),
158 remove_loaded_file_(false), status_(WaitingToLoad)
162 void CacheItem::Impl::setStatus(ImageStatus new_status)
164 if (status_ == new_status)
167 status_ = new_status;
172 void CacheItem::Impl::imageConverted(bool success)
174 string const text = success ? "succeeded" : "failed";
175 lyxerr[Debug::GRAPHICS] << "Image conversion " << text << "." << endl;
177 file_to_load_ = converter_.get() ?
178 converter_->convertedFile() : string();
182 success = !file_to_load_.empty() && IsFileReadable(file_to_load_);
183 lyxerr[Debug::GRAPHICS] << "Unable to find converted file!" << endl;
186 setStatus(ErrorConverting);
189 lyx::unlink(unzipped_filename_);
198 // This function gets called from the callback after the image has been
199 // converted successfully.
200 void CacheItem::Impl::loadImage()
203 lyxerr[Debug::GRAPHICS] << "Loading image." << endl;
205 image_ = Image::newImage();
208 cl_ = image_->finishedLoading.connect(
209 boost::bind(&Impl::imageLoaded, this, _1));
210 image_->load(file_to_load_);
214 void CacheItem::Impl::imageLoaded(bool success)
216 string const text = success ? "succeeded" : "failed";
217 lyxerr[Debug::GRAPHICS] << "Image loading " << text << "." << endl;
219 // Clean up after loading.
221 lyx::unlink(unzipped_filename_);
223 if (remove_loaded_file_ && unzipped_filename_ != file_to_load_)
224 lyx::unlink(file_to_load_);
229 setStatus(ErrorLoading);
233 // Inform the outside world.
243 string const findTargetFormat(string const & from)
245 typedef grfx::Image::FormatList FormatList;
246 FormatList const formats = grfx::Image::loadableFormats();
248 // There must be a format to load from.
249 lyx::Assert(!formats.empty());
251 // First ascertain if we can load directly with no conversion
252 FormatList::const_iterator it1 = formats.begin();
253 FormatList::const_iterator end = formats.end();
254 for (; it1 != end; ++it1) {
259 // So, we have to convert to a loadable format. Can we?
260 FormatList::const_iterator it2 = formats.begin();
261 for (; it2 != end; ++it2) {
262 if (grfx::Converter::isReachable(from, *it2))
266 // Failed! so we have to try to convert it to XPM format
267 // with the standard converter
268 return string("xpm");
276 void CacheItem::Impl::convertToDisplayFormat()
278 setStatus(Converting);
279 // Make a local copy in case we unzip it
280 string const filename = zippedFile(filename_) ?
281 unzipFile(filename_) : filename_;
282 string const displayed_filename = MakeDisplayPath(filename_);
283 lyxerr[Debug::GRAPHICS] << "[GrahicsCacheItem::convertToDisplayFormat]\n"
284 << "\tAttempting to convert image file: " << filename
285 << "\n\twith displayed filename: " << displayed_filename
288 // First, check that the file exists!
289 if (!IsFileReadable(filename)) {
290 setStatus(ErrorNoFile);
291 lyxerr[Debug::GRAPHICS] << "\tThe file is not readable" << endl;
295 string from = getExtFromContents(filename);
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_->connect(boost::bind(&Impl::imageConverted, this, _1));
325 converter_->startConversion();