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