]> git.lyx.org Git - lyx.git/blob - src/graphics/GraphicsCacheItem.C
Pedantic change of email only.
[lyx.git] / src / graphics / GraphicsCacheItem.C
1 /*
2  * \file GraphicsCacheItem.C
3  * Copyright 2002 the LyX Team
4  * Read the file COPYING
5  *
6  * \author Baruch Even <baruch.even@writeme.com>
7  * \author Herbert Voss <voss@lyx.org>
8  * \author Angus Leeming <leeming@lyx.org>
9  */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include "graphics/GraphicsCacheItem.h"
18 #include "graphics/GraphicsImage.h"
19 #include "graphics/GraphicsConverter.h"
20
21 #include "debug.h"
22
23 #include "support/LAssert.h"
24 #include "support/filetools.h"
25
26 #include <boost/shared_ptr.hpp>
27 #include <boost/bind.hpp>
28 #include <boost/signals/connection.hpp>
29 #include <boost/signals/trackable.hpp>
30
31 using std::endl;
32
33 namespace grfx {
34
35 struct CacheItem::Impl : public boost::signals::trackable {
36
37         ///
38         Impl(CacheItem &, string const & file);
39
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.
46          *
47          *  If no file conversion is needed, then convertToDisplayFormat() calls
48          *  loadImage() directly.
49          *
50          *  convertToDisplayFormat() will set the loading status flag as
51          *  approriate through calls to setStatus().
52          */
53         void convertToDisplayFormat();
54
55         /** Load the image into memory. This is called either from
56          *  convertToDisplayFormat() direct or from imageConverted().
57          */
58         void loadImage();
59
60         /** Get a notification when the image conversion is done.
61          *  Connected to a signal on_finish_ which is passed to
62          *  Converter::convert.
63          */
64         void imageConverted(bool);
65
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.
69          */
70         void imageLoaded(bool);
71
72         /** Sets the status of the loading process. Also notifies
73          *  listeners that the status has chacnged.
74          */
75         void setStatus(ImageStatus new_status);
76
77         ///
78         CacheItem & parent_;
79
80         /// The filename we refer too.
81         string filename_;
82         /// Is the file compressed?
83         bool zipped_;
84         /// If so, store the uncompressed file in this temporary file.
85         string unzipped_filename_;
86         /// What file are we trying to load?
87         string file_to_load_;
88         /** Should we delete the file after loading? True if the file is
89          *  the result of a conversion process.
90          */
91         bool remove_loaded_file_;
92
93         /// The image and its loading status.
94         boost::shared_ptr<Image> image_;
95         ///
96         ImageStatus status_;
97
98         /// The connection to the signal Image::finishedLoading
99         boost::signals::connection cl_;
100
101         /// The connection of the signal ConvProcess::finishedConversion,
102         boost::signals::connection cc_;
103
104         ///
105         boost::scoped_ptr<Converter> converter_;
106 };
107
108
109 CacheItem::CacheItem(string const & file)
110         : pimpl_(new Impl(*this, file))
111 {}
112
113                  
114 CacheItem::~CacheItem()
115 {}
116
117
118 string const & CacheItem::filename() const
119 {
120         return pimpl_->filename_;
121 }
122
123
124 void CacheItem::startLoading()
125 {
126         if (pimpl_->status_ != WaitingToLoad)
127                 return;
128
129         pimpl_->convertToDisplayFormat();
130 }
131
132
133 Image const * CacheItem::image() const
134 {
135         return pimpl_->image_.get();
136 }
137
138
139 ImageStatus CacheItem::status() const
140 {
141         return pimpl_->status_;
142 }
143
144
145 //------------------------------
146 // Implementation details follow
147 //------------------------------
148
149
150 CacheItem::Impl::Impl(CacheItem & p, string const & file)
151         : parent_(p), filename_(file), zipped_(false),
152           remove_loaded_file_(false), status_(WaitingToLoad)
153 {}
154
155
156 void CacheItem::Impl::setStatus(ImageStatus new_status)
157 {
158         if (status_ == new_status)
159                 return;
160
161         status_ = new_status;
162         parent_.statusChanged();
163 }
164
165
166 void CacheItem::Impl::imageConverted(bool success)
167 {
168         string const text = success ? "succeeded" : "failed";
169         lyxerr[Debug::GRAPHICS] << "Image conversion " << text << "." << endl;
170
171         file_to_load_ = converter_.get() ?
172                 converter_->convertedFile() : string();
173         converter_.reset();
174         cc_.disconnect();
175         
176         success = !file_to_load_.empty() && IsFileReadable(file_to_load_);
177         lyxerr[Debug::GRAPHICS] << "Unable to find converted file!" << endl;
178
179         if (!success) {
180                 setStatus(ErrorConverting);
181
182                 if (zipped_)
183                         lyx::unlink(unzipped_filename_);
184
185                 return;
186         }
187
188         loadImage();
189 }
190
191
192 // This function gets called from the callback after the image has been
193 // converted successfully.
194 void CacheItem::Impl::loadImage()
195 {
196         setStatus(Loading);
197         lyxerr[Debug::GRAPHICS] << "Loading image." << endl;
198
199         image_ = Image::newImage();
200
201         cl_.disconnect();
202         cl_ = image_->finishedLoading.connect(
203                 boost::bind(&Impl::imageLoaded, this, _1));
204         image_->load(file_to_load_);
205 }
206
207
208 void CacheItem::Impl::imageLoaded(bool success)
209 {
210         string const text = success ? "succeeded" : "failed";
211         lyxerr[Debug::GRAPHICS] << "Image loading " << text << "." << endl;
212
213         // Clean up after loading.
214         if (zipped_)
215                 lyx::unlink(unzipped_filename_);
216
217         if (remove_loaded_file_ && unzipped_filename_ != file_to_load_)
218                 lyx::unlink(file_to_load_);
219
220         cl_.disconnect();
221
222         if (!success) {
223                 setStatus(ErrorLoading);
224                 return;
225         }
226
227         setStatus(Loaded);
228 }
229
230 } // namespace grfx
231
232
233 namespace {
234
235 string const findTargetFormat(string const & from)
236 {
237         typedef grfx::Image::FormatList FormatList;
238         FormatList const formats = grfx::Image::loadableFormats();
239
240         // There must be a format to load from.
241         lyx::Assert(!formats.empty());
242
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) {
247                 if (from == *it1)
248                         return *it1;
249         }
250
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))
255                         return *it2;
256         }
257
258         // Failed! so we have to try to convert it to XPM format
259         // with the standard converter
260         return string("xpm");
261 }
262
263 } // anon namespace
264
265
266 namespace grfx {
267
268 void CacheItem::Impl::convertToDisplayFormat()
269 {
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
278                 << endl;
279
280         // First, check that the file exists!
281         if (!IsFileReadable(filename)) {
282                 setStatus(ErrorNoFile);
283                 lyxerr[Debug::GRAPHICS] << "\tThe file is not readable" << endl;
284                 return;
285         }
286
287         string from = getExtFromContents(filename);
288         // Some old ps-files make problems, so we do not need direct
289         // loading of an ps-file
290         if (from == "ps") {
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";
294                 from = "eps";
295         } else
296                 lyxerr[Debug::GRAPHICS] 
297                         << "\n\tThe file contains " << from << " format data." << endl;
298         string const to = findTargetFormat(from);
299
300         if (from == to) {
301                 // No conversion needed!
302                 lyxerr[Debug::GRAPHICS] << "\tNo conversion needed (from == to)!" << endl;
303                 file_to_load_ = filename;
304                 loadImage();
305                 return;
306         }
307
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());
311
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;
316
317         // Remove the temp file, we only want the name...
318         lyx::unlink(to_file_base);
319
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();
327 }
328
329 } // namespace grfx