]> git.lyx.org Git - lyx.git/blob - src/graphics/GraphicsLoader.cpp
stupid me...
[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 <boost/bind.hpp>
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_.size()) {
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(boost::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 boost::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_.get())
277                 return;
278         pimpl_->startLoading();
279 }
280
281
282 void Loader::startMonitoring() const
283 {
284         if (!pimpl_->cached_item_.get())
285                 return;
286
287         pimpl_->cached_item_->startMonitoring();
288 }
289
290
291 bool Loader::monitoring() const
292 {
293         if (!pimpl_->cached_item_.get())
294                 return false;
295
296         return pimpl_->cached_item_->monitoring();
297 }
298
299
300 unsigned long Loader::checksum() const
301 {
302         if (!pimpl_->cached_item_.get())
303                 return 0;
304
305         return pimpl_->cached_item_->checksum();
306 }
307
308
309 FileName const & Loader::filename() const
310 {
311         static FileName const empty;
312         return pimpl_->cached_item_.get() ?
313                 pimpl_->cached_item_->filename() : empty;
314 }
315
316
317 ImageStatus Loader::status() const
318 {
319         return pimpl_->status_;
320 }
321
322
323 boost::signals::connection Loader::connect(slot_type const & slot) const
324 {
325         return pimpl_->signal_.connect(slot);
326 }
327
328
329 Image const * Loader::image() const
330 {
331         return pimpl_->image_.get();
332 }
333
334
335 Loader::Impl::Impl()
336         : status_(WaitingToLoad)
337 {
338 }
339
340
341 Loader::Impl::~Impl()
342 {
343         resetFile(FileName());
344 }
345
346
347 void Loader::Impl::resetFile(FileName const & file)
348 {
349         FileName const old_file = cached_item_.get() ?
350                 cached_item_->filename() : FileName();
351
352         if (file == old_file)
353                 return;
354
355         // If monitoring() the current file, should continue to monitor the
356         // new file.
357         bool continue_monitoring = false;
358
359         if (!old_file.empty()) {
360                 continue_monitoring = cached_item_->monitoring();
361                 // cached_item_ is going to be reset, so the connected
362                 // signal needs to be disconnected.
363                 sc_.disconnect();
364                 cached_item_.reset();
365                 Cache::get().remove(old_file);
366         }
367
368         status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
369         image_.reset();
370
371         if (cached_item_.get() || file.empty())
372                 return;
373
374         Cache & gc = Cache::get();
375         if (!gc.inCache(file))
376                 gc.add(file);
377
378         // We /must/ make a local copy of this.
379         cached_item_ = gc.item(file);
380         status_ = cached_item_->status();
381
382         if (continue_monitoring && !cached_item_->monitoring())
383                 cached_item_->startMonitoring();
384
385         sc_ = cached_item_->connect(boost::bind(&Impl::statusChanged, this));
386 }
387
388
389 void Loader::Impl::resetParams(Params const & params)
390 {
391         if (params == params_)
392                 return;
393
394         params_ = params;
395         status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
396         image_.reset();
397 }
398
399
400 void Loader::Impl::statusChanged()
401 {
402         status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
403         createPixmap();
404         signal_();
405 }
406
407
408 void Loader::Impl::createPixmap()
409 {
410         if (!params_.display || status_ != Loaded)
411                 return;
412
413         if (!cached_item_.get()) {
414                 LYXERR(Debug::GRAPHICS, "pixmap not cached yet");
415                 return;
416         }
417
418         if (!cached_item_->image()) {
419                 // There must have been a problem reading the file.
420                 LYXERR(Debug::GRAPHICS, "Graphics file not loaded.");
421                 return;
422         }
423
424         image_.reset(cached_item_->image()->clone());
425
426         bool const success = image_->setPixmap(params_);
427
428         if (success) {
429                 status_ = Ready;
430         } else {
431                 image_.reset();
432                 status_ = ErrorGeneratingPixmap;
433         }
434 }
435
436 void Loader::Impl::startLoading()
437 {
438         if (status_ != WaitingToLoad)
439                 return;
440
441         if (cached_item_->tryDisplayFormat()) {
442                 status_ = Loaded;
443                 createPixmap();
444                 return;
445         }
446
447         LoaderQueue::get().touch(cached_item_);
448 }
449
450
451 } // namespace graphics
452 } // namespace lyx