]> git.lyx.org Git - lyx.git/blob - src/graphics/GraphicsCacheItem.C
Removed all redundant using directives from the source.
[lyx.git] / src / graphics / GraphicsCacheItem.C
1 /**
2  * \file GraphicsCacheItem.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Baruch Even
7  * \author Herbert Voß
8  * \author Angus Leeming
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "GraphicsCacheItem.h"
16 #include "GraphicsConverter.h"
17 #include "GraphicsImage.h"
18
19 #include "debug.h"
20
21 #include "support/filetools.h"
22 #include "support/FileMonitor.h"
23 #include "support/LAssert.h"
24 #include "support/lyxlib.h"
25
26 #include <boost/bind.hpp>
27
28 namespace support = lyx::support;
29
30 using support::Assert;
31 using support::ChangeExtension;
32 using support::FileMonitor;
33 using support::IsFileReadable;
34 using support::MakeDisplayPath;
35 using support::OnlyFilename;
36 using support::getExtFromContents;
37 using support::tempName;
38 using support::unlink;
39 using support::unzipFile;
40 using support::zippedFile;
41
42 using std::endl;
43
44 namespace lyx {
45 namespace graphics {
46
47 struct CacheItem::Impl : public boost::signals::trackable {
48
49         ///
50         Impl(string const & file);
51
52         /** Start the image conversion process, checking first that it is
53          *  necessary. If it is necessary, then a conversion task is started.
54          *  CacheItem asumes that the conversion is asynchronous and so
55          *  passes a Signal to the converting routine. When the conversion
56          *  is finished, this Signal is emitted, returning the converted
57          *  file to this->imageConverted.
58          *
59          *  If no file conversion is needed, then convertToDisplayFormat() calls
60          *  loadImage() directly.
61          *
62          *  convertToDisplayFormat() will set the loading status flag as
63          *  approriate through calls to setStatus().
64          */
65         void convertToDisplayFormat();
66
67         /** Load the image into memory. This is called either from
68          *  convertToDisplayFormat() direct or from imageConverted().
69          */
70         void loadImage();
71
72         /** Get a notification when the image conversion is done.
73          *  Connected to a signal on_finish_ which is passed to
74          *  Converter::convert.
75          */
76         void imageConverted(bool);
77
78         /** Get a notification when the image loading is done.
79          *  Connected to a signal on_finish_ which is passed to
80          *  lyx::graphics::Image::loadImage.
81          */
82         void imageLoaded(bool);
83
84         /** Sets the status of the loading process. Also notifies
85          *  listeners that the status has changed.
86          */
87         void setStatus(ImageStatus new_status);
88
89         /** Can be invoked directly by the user, but is also connected to the
90          *  FileMonitor and so is invoked when the file is changed
91          *  (if monitoring is taking place).
92          */
93         void startLoading();
94
95         /** If we are asked to load the file for a second or further time,
96          *  (because the file has changed), then we'll have to first reset
97          *  many of the variables below.
98          */
99         void reset();
100
101         /// The filename we refer too.
102         string const filename_;
103         ///
104         FileMonitor const monitor_;
105
106         /// Is the file compressed?
107         bool zipped_;
108         /// If so, store the uncompressed file in this temporary file.
109         string unzipped_filename_;
110         /// What file are we trying to load?
111         string file_to_load_;
112         /** Should we delete the file after loading? True if the file is
113          *  the result of a conversion process.
114          */
115         bool remove_loaded_file_;
116
117         /// The image and its loading status.
118         boost::shared_ptr<Image> image_;
119         ///
120         ImageStatus status_;
121
122         /// This signal is emitted when the image loading status changes.
123         boost::signal0<void> statusChanged;
124
125         /// The connection to the signal Image::finishedLoading
126         boost::signals::connection cl_;
127
128         /// The connection of the signal ConvProcess::finishedConversion,
129         boost::signals::connection cc_;
130
131         ///
132         boost::scoped_ptr<Converter> converter_;
133 };
134
135
136 CacheItem::CacheItem(string const & file)
137         : pimpl_(new Impl(file))
138 {}
139
140
141 CacheItem::~CacheItem()
142 {}
143
144
145 string const & CacheItem::filename() const
146 {
147         return pimpl_->filename_;
148 }
149
150
151 void CacheItem::startLoading() const
152 {
153         pimpl_->startLoading();
154 }
155
156
157 void CacheItem::startMonitoring() const
158 {
159         if (!pimpl_->monitor_.monitoring())
160                 pimpl_->monitor_.start();
161 }
162
163
164 bool CacheItem::monitoring() const
165 {
166         return pimpl_->monitor_.monitoring();
167 }
168
169
170 unsigned long CacheItem::checksum() const
171 {
172         return pimpl_->monitor_.checksum();
173 }
174
175
176 Image const * CacheItem::image() const
177 {
178         return pimpl_->image_.get();
179 }
180
181
182 ImageStatus CacheItem::status() const
183 {
184         return pimpl_->status_;
185 }
186
187
188 boost::signals::connection CacheItem::connect(slot_type const & slot) const
189 {
190         return pimpl_->statusChanged.connect(slot);
191 }
192
193
194 //------------------------------
195 // Implementation details follow
196 //------------------------------
197
198
199 CacheItem::Impl::Impl(string const & file)
200         : filename_(file),
201           monitor_(file, 2000),
202           zipped_(false),
203           remove_loaded_file_(false),
204           status_(WaitingToLoad)
205 {
206         monitor_.connect(boost::bind(&Impl::startLoading, this));
207 }
208
209
210 void CacheItem::Impl::startLoading()
211 {
212         if (status_ != WaitingToLoad)
213                 reset();
214
215         convertToDisplayFormat();
216 }
217
218
219 void CacheItem::Impl::reset()
220 {
221         zipped_ = false;
222         if (!unzipped_filename_.empty())
223                 unlink(unzipped_filename_);
224         unzipped_filename_.erase();
225
226         if (remove_loaded_file_ && !file_to_load_.empty())
227                 unlink(file_to_load_);
228         remove_loaded_file_ = false;
229         file_to_load_.erase();
230
231         if (image_.get())
232                 image_.reset();
233
234         status_ = WaitingToLoad;
235
236         if (cl_.connected())
237                 cl_.disconnect();
238
239         if (cc_.connected())
240                 cc_.disconnect();
241
242         if (converter_.get())
243                 converter_.reset();
244 }
245
246
247 void CacheItem::Impl::setStatus(ImageStatus new_status)
248 {
249         if (status_ == new_status)
250                 return;
251
252         status_ = new_status;
253         statusChanged();
254 }
255
256
257 void CacheItem::Impl::imageConverted(bool success)
258 {
259         string const text = success ? "succeeded" : "failed";
260         lyxerr[Debug::GRAPHICS] << "Image conversion " << text << '.' << endl;
261
262         file_to_load_ = converter_.get() ?
263                 converter_->convertedFile() : string();
264         converter_.reset();
265         cc_.disconnect();
266
267         success = !file_to_load_.empty() && IsFileReadable(file_to_load_);
268
269         if (!success) {
270                 lyxerr[Debug::GRAPHICS] << "Unable to find converted file!"
271                                         << endl;
272                 setStatus(ErrorConverting);
273
274                 if (zipped_)
275                         unlink(unzipped_filename_);
276
277                 return;
278         }
279
280         loadImage();
281 }
282
283
284 // This function gets called from the callback after the image has been
285 // converted successfully.
286 void CacheItem::Impl::loadImage()
287 {
288         setStatus(Loading);
289         lyxerr[Debug::GRAPHICS] << "Loading image." << endl;
290
291         image_ = Image::newImage();
292
293         cl_.disconnect();
294         cl_ = image_->finishedLoading.connect(
295                 boost::bind(&Impl::imageLoaded, this, _1));
296         image_->load(file_to_load_);
297 }
298
299
300 void CacheItem::Impl::imageLoaded(bool success)
301 {
302         string const text = success ? "succeeded" : "failed";
303         lyxerr[Debug::GRAPHICS] << "Image loading " << text << '.' << endl;
304
305         // Clean up after loading.
306         if (zipped_)
307                 unlink(unzipped_filename_);
308
309         if (remove_loaded_file_ && unzipped_filename_ != file_to_load_)
310                 unlink(file_to_load_);
311
312         cl_.disconnect();
313
314         if (!success) {
315                 setStatus(ErrorLoading);
316                 return;
317         }
318
319         // Inform the outside world.
320         setStatus(Loaded);
321 }
322
323
324 } // namespace graphics
325 } // namespace lyx
326
327
328 namespace {
329
330 string const findTargetFormat(string const & from)
331 {
332         typedef lyx::graphics::Image::FormatList FormatList;
333         FormatList const formats = lyx::graphics::Image::loadableFormats();
334
335         // There must be a format to load from.
336         Assert(!formats.empty());
337
338         // First ascertain if we can load directly with no conversion
339         FormatList::const_iterator it  = formats.begin();
340         FormatList::const_iterator end = formats.end();
341         for (; it != end; ++it) {
342                 if (from == *it)
343                         return *it;
344         }
345
346         // So, we have to convert to a loadable format. Can we?
347         it = formats.begin();
348         for (; it != end; ++it) {
349                 if (lyx::graphics::Converter::isReachable(from, *it))
350                         return *it;
351                 else
352                         lyxerr[Debug::GRAPHICS]
353                                 << "Unable to convert from " << from
354                                 << " to " << *it << std::endl;
355         }
356
357         // Failed! so we have to try to convert it to PPM format
358         // with the standard converter
359         return string("ppm");
360 }
361
362 } // anon namespace
363
364
365 namespace lyx {
366 namespace graphics {
367
368 void CacheItem::Impl::convertToDisplayFormat()
369 {
370         setStatus(Converting);
371
372         // First, check that the file exists!
373         if (!IsFileReadable(filename_)) {
374                 if (status_ != ErrorNoFile) {
375                         setStatus(ErrorNoFile);
376                         lyxerr[Debug::GRAPHICS]
377                                 << "\tThe file is not readable" << endl;
378                 }
379                 return;
380         }
381
382         // Make a local copy in case we unzip it
383         string const filename = zippedFile(filename_) ?
384                 unzipFile(filename_) : filename_;
385         string const displayed_filename = MakeDisplayPath(filename_);
386         lyxerr[Debug::GRAPHICS] << "[GrahicsCacheItem::convertToDisplayFormat]\n"
387                 << "\tAttempting to convert image file: " << filename
388                 << "\n\twith displayed filename: " << displayed_filename
389                 << endl;
390
391         string from = getExtFromContents(filename);
392         lyxerr[Debug::GRAPHICS]
393                 << "\n\tThe file contains " << from << " format data." << endl;
394         string const to = findTargetFormat(from);
395
396         if (from == to) {
397                 // No conversion needed!
398                 lyxerr[Debug::GRAPHICS] << "\tNo conversion needed (from == to)!" << endl;
399                 file_to_load_ = filename;
400                 loadImage();
401                 return;
402         }
403
404         lyxerr[Debug::GRAPHICS] << "\tConverting it to " << to << " format." << endl;
405         // Take only the filename part of the file, without path or extension.
406         string const temp = ChangeExtension(OnlyFilename(filename), string());
407
408         // Add some stuff to create a uniquely named temporary file.
409         // This file is deleted in loadImage after it is loaded into memory.
410         string const to_file_base = tempName(string(), temp);
411         remove_loaded_file_ = true;
412
413         // Remove the temp file, we only want the name...
414         unlink(to_file_base);
415
416         // Connect a signal to this->imageConverted and pass this signal to
417         // the graphics converter so that we can load the modified file
418         // on completion of the conversion process.
419         converter_.reset(new Converter(filename, to_file_base, from, to));
420         converter_->connect(boost::bind(&Impl::imageConverted, this, _1));
421         converter_->startConversion();
422 }
423
424 } // namespace graphics
425 } // namespace lyx