]> git.lyx.org Git - lyx.git/blob - src/graphics/GraphicsLoader.cpp
f635e0b321f6b8e625f53bf469d8811adce36779
[lyx.git] / src / graphics / GraphicsLoader.cpp
1 /**
2  * \file GraphicsLoader.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "GraphicsLoader.h"
14
15 #include "GraphicsCacheItem.h"
16 #include "GraphicsImage.h"
17 #include "GraphicsParams.h"
18 #include "GraphicsCache.h"
19
20 #include "support/debug.h"
21 #include "support/Timeout.h"
22
23 #include "support/bind.h"
24
25 #include <set>
26 #include <queue>
27
28 using namespace std;
29 using namespace lyx::support;
30
31 namespace lyx {
32 namespace graphics {
33
34
35 /////////////////////////////////////////////////////////////////////
36 //
37 // LoaderQueue
38 //
39 /////////////////////////////////////////////////////////////////////
40
41 class LoaderQueue {
42 public:
43         /// Use this to request that the item is loaded.
44         void touch(Cache::ItemPtr const & item);
45         /// Query whether the clock is ticking.
46         bool running() const;
47         ///get the and only instance of the class
48         static LoaderQueue & get();
49 private:
50         /// This class is a singleton class... use LoaderQueue::get() instead
51         LoaderQueue();
52         /// The in-progress loading queue (elements are unique here).
53         list<Cache::ItemPtr> cache_queue_;
54         /// Used to make the insertion of new elements faster.
55         set<Cache::ItemPtr> cache_set_;
56         /// Newly touched elements go here. loadNext moves them to cache_queue_
57         queue<Cache::ItemPtr> bucket_;
58         ///
59         Timeout timer;
60         ///
61         bool running_;
62
63         /** This is the 'threaded' method, that does the loading in the
64          *  background.
65          */
66         void loadNext();
67         ///
68         void startLoader();
69         ///
70         void stopLoader();
71 };
72
73
74 //static int s_numimages_ = 5;
75 //static int s_millisecs_ = 500;
76
77 static int s_numimages_ = 10;
78 static int s_millisecs_ = 500;
79
80 LoaderQueue & LoaderQueue::get()
81 {
82         static LoaderQueue singleton;
83         return singleton;
84 }
85
86
87 void LoaderQueue::loadNext()
88 {
89         LYXERR(Debug::GRAPHICS, "LoaderQueue: "
90                 << cache_queue_.size() << " items in the queue");
91         int counter = s_numimages_;
92         while (cache_queue_.size() && counter--) {
93                 Cache::ItemPtr ptr = cache_queue_.front();
94                 cache_set_.erase(ptr);
95                 cache_queue_.pop_front();
96                 if (ptr->status() == WaitingToLoad)
97                         ptr->startLoading();
98         }
99         if (!cache_queue_.empty()) {
100                 startLoader();
101         } else {
102                 stopLoader();
103         }
104 }
105
106
107 LoaderQueue::LoaderQueue() : timer(s_millisecs_, Timeout::ONETIME),
108                              running_(false)
109 {
110         timer.timeout.connect(bind(&LoaderQueue::loadNext, this));
111 }
112
113
114 void LoaderQueue::startLoader()
115 {
116         LYXERR(Debug::GRAPHICS, "LoaderQueue: waking up");
117         running_ = true ;
118         timer.setTimeout(s_millisecs_);
119         timer.start();
120 }
121
122
123 void LoaderQueue::stopLoader()
124 {
125         timer.stop();
126         running_ = false ;
127         LYXERR(Debug::GRAPHICS, "LoaderQueue: I'm going to sleep");
128 }
129
130
131 bool LoaderQueue::running() const
132 {
133         return running_ ;
134 }
135
136
137 void LoaderQueue::touch(Cache::ItemPtr const & item)
138 {
139         if (! cache_set_.insert(item).second) {
140                 list<Cache::ItemPtr>::iterator
141                         it = cache_queue_.begin();
142                 list<Cache::ItemPtr>::iterator
143                         end = cache_queue_.end();
144
145                 it = find(it, end, item);
146                 if (it != end)
147                         cache_queue_.erase(it);
148         }
149         cache_queue_.push_front(item);
150         if (!running_)
151                 startLoader();
152 }
153
154
155
156 /////////////////////////////////////////////////////////////////////
157 //
158 // GraphicsLoader
159 //
160 /////////////////////////////////////////////////////////////////////
161
162 typedef shared_ptr<Image> ImagePtr;
163
164 class Loader::Impl : public boost::signals::trackable {
165 public:
166         ///
167         Impl();
168         ///
169         ~Impl();
170         ///
171         void resetFile(FileName const &);
172         ///
173         void resetParams(Params const &);
174         ///
175         void createPixmap();
176         ///
177         void startLoading();
178         ///
179         Params const & params() const { return params_; }
180
181         /// The loading status of the image.
182         ImageStatus status_;
183         /** Must store a copy of the cached item to ensure that it is not
184          *  erased unexpectedly by the cache itself.
185          */
186         Cache::ItemPtr cached_item_;
187         /// We modify a local copy of the image once it is loaded.
188         ImagePtr image_;
189         /// This signal is emitted when the image loading status changes.
190         boost::signal<void()> signal_;
191         /// The connection of the signal StatusChanged  
192         boost::signals::connection sc_;
193
194 private:
195         ///
196         void statusChanged();
197         ///
198         void checkedLoading();
199
200         ///
201         Params params_;
202 };
203
204
205 Loader::Loader()
206         : pimpl_(new Impl)
207 {}
208
209
210 Loader::Loader(FileName const & file, bool display)
211         : pimpl_(new Impl)
212 {
213         reset(file, display);
214 }
215
216
217 Loader::Loader(FileName const & file, Params const & params)
218         : pimpl_(new Impl)
219 {
220         reset(file, params);
221 }
222
223
224 Loader::Loader(Loader const & other)
225         : pimpl_(new Impl)
226 {
227         Params const & params = other.pimpl_->params();
228         reset(params.filename, params);
229 }
230
231
232 Loader::~Loader()
233 {
234         delete pimpl_;
235 }
236
237
238 Loader & Loader::operator=(Loader const & other)
239 {
240         if (this != &other) {
241                 Params const & params = other.pimpl_->params();
242                 reset(params.filename, params);
243         }
244         return *this;
245 }
246
247
248 void Loader::reset(FileName const & file, bool display) const
249 {
250         Params params;
251         params.display = display;
252         pimpl_->resetParams(params);
253
254         pimpl_->resetFile(file);
255         pimpl_->createPixmap();
256 }
257
258
259 void Loader::reset(FileName const & file, Params const & params) const
260 {
261         pimpl_->resetParams(params);
262         pimpl_->resetFile(file);
263         pimpl_->createPixmap();
264 }
265
266
267 void Loader::reset(Params const & params) const
268 {
269         pimpl_->resetParams(params);
270         pimpl_->createPixmap();
271 }
272
273
274 void Loader::startLoading() const
275 {
276         if (pimpl_->status_ != WaitingToLoad || !pimpl_->cached_item_)
277                 return;
278         pimpl_->startLoading();
279 }
280
281
282 void Loader::reload() const 
283 {
284         pimpl_->cached_item_->startLoading();
285 }
286
287
288 void Loader::startMonitoring() const
289 {
290         if (!pimpl_->cached_item_)
291                 return;
292
293         pimpl_->cached_item_->startMonitoring();
294 }
295
296
297 bool Loader::monitoring() const
298 {
299         if (!pimpl_->cached_item_)
300                 return false;
301
302         return pimpl_->cached_item_->monitoring();
303 }
304
305
306 unsigned long Loader::checksum() const
307 {
308         if (!pimpl_->cached_item_)
309                 return 0;
310
311         return pimpl_->cached_item_->checksum();
312 }
313
314
315 FileName const & Loader::filename() const
316 {
317         static FileName const empty;
318         return pimpl_->cached_item_ ?
319                 pimpl_->cached_item_->filename() : empty;
320 }
321
322
323 ImageStatus Loader::status() const
324 {
325         return pimpl_->status_;
326 }
327
328
329 boost::signals::connection Loader::connect(slot_type const & slot) const
330 {
331         return pimpl_->signal_.connect(slot);
332 }
333
334
335 Image const * Loader::image() const
336 {
337         return pimpl_->image_.get();
338 }
339
340
341 Loader::Impl::Impl()
342         : status_(WaitingToLoad)
343 {
344 }
345
346
347 Loader::Impl::~Impl()
348 {
349         resetFile(FileName());
350 }
351
352
353 void Loader::Impl::resetFile(FileName const & file)
354 {
355         FileName const old_file = cached_item_ ?
356                 cached_item_->filename() : FileName();
357
358         if (file == old_file)
359                 return;
360
361         // If monitoring() the current file, should continue to monitor the
362         // new file.
363         bool continue_monitoring = false;
364
365         if (!old_file.empty()) {
366                 continue_monitoring = cached_item_->monitoring();
367                 // cached_item_ is going to be reset, so the connected
368                 // signal needs to be disconnected.
369                 sc_.disconnect();
370                 cached_item_.reset();
371                 if (status_ != Converting) {
372                         Cache::get().remove(old_file);
373                 } else {
374                         //TODO remove cache item when it is not busy any more, see #7163
375                 }
376         }
377
378         status_ = cached_item_ ? cached_item_->status() : WaitingToLoad;
379         image_.reset();
380
381         if (cached_item_ || file.empty())
382                 return;
383
384         Cache & gc = Cache::get();
385         if (!gc.inCache(file))
386                 gc.add(file);
387
388         // We /must/ make a local copy of this.
389         cached_item_ = gc.item(file);
390         status_ = cached_item_->status();
391
392         if (continue_monitoring && !cached_item_->monitoring())
393                 cached_item_->startMonitoring();
394
395         sc_ = cached_item_->connect(bind(&Impl::statusChanged, this));
396 }
397
398
399 void Loader::Impl::resetParams(Params const & params)
400 {
401         if (params == params_)
402                 return;
403
404         params_ = params;
405         status_ = cached_item_ ? cached_item_->status() : WaitingToLoad;
406         image_.reset();
407 }
408
409
410 void Loader::Impl::statusChanged()
411 {
412         status_ = cached_item_ ? cached_item_->status() : WaitingToLoad;
413         createPixmap();
414         signal_();
415 }
416
417
418 void Loader::Impl::createPixmap()
419 {
420         if (!params_.display || status_ != Loaded)
421                 return;
422
423         if (!cached_item_) {
424                 LYXERR(Debug::GRAPHICS, "pixmap not cached yet");
425                 return;
426         }
427
428         if (!cached_item_->image()) {
429                 // There must have been a problem reading the file.
430                 LYXERR(Debug::GRAPHICS, "Graphics file not loaded.");
431                 return;
432         }
433
434         image_.reset(cached_item_->image()->clone());
435
436         bool const success = image_->setPixmap(params_);
437
438         if (success) {
439                 status_ = Ready;
440         } else {
441                 image_.reset();
442                 status_ = ErrorGeneratingPixmap;
443         }
444 }
445
446 void Loader::Impl::startLoading()
447 {
448         if (status_ != WaitingToLoad)
449                 return;
450
451         if (cached_item_->tryDisplayFormat()) {
452                 status_ = Loaded;
453                 createPixmap();
454                 return;
455         }
456
457         LoaderQueue::get().touch(cached_item_);
458 }
459
460
461 } // namespace graphics
462 } // namespace lyx