]> git.lyx.org Git - lyx.git/blob - src/graphics/GraphicsLoader.C
c819f4379fef9ab33960e81dea240209ac2d755c
[lyx.git] / src / graphics / GraphicsLoader.C
1 /*
2  * \file GraphicsLoader.C
3  * Copyright 2002 the LyX Team
4  * Read the file COPYING
5  *
6  * \author Angus Leeming <leeming@lyx.org>
7  */
8
9 #include <config.h>
10
11 #ifdef __GNUG__
12 #pragma implementation
13 #endif
14
15 #include "GraphicsLoader.h"
16 #include "GraphicsCache.h"
17 #include "GraphicsCacheItem.h"
18 #include "GraphicsImage.h"
19 #include "GraphicsParams.h"
20 #include "GraphicsSupport.h"
21
22 #include "frontends/Timeout.h"
23
24 #include <boost/bind.hpp>
25 #include <boost/signals/trackable.hpp>
26
27 #include <list>
28
29 namespace grfx {
30
31 struct Loader::Impl : boost::signals::trackable {
32         ///
33         Impl(Params const &);
34         ///
35         ~Impl();
36         ///
37         void resetFile(string const &);
38         ///
39         void resetParams(Params const &);
40         ///
41         void createPixmap();
42
43         ///
44         void startLoading(Inset const &, BufferView const &);
45
46         /// The loading status of the image.
47         ImageStatus status_;
48         /** Must store a copy of the cached item to ensure that it is not
49          *  erased unexpectedly by the cache itself.
50          */
51         Cache::ItemPtr cached_item_;
52         /// We modify a local copy of the image once it is loaded.
53         Image::ImagePtr image_;
54         /// This signal is emitted when the image loading status changes.
55         boost::signal0<void> signal_;
56
57 private:
58         ///
59         void statusChanged();
60         ///
61         void checkedLoading();
62
63         ///
64         Params params_;
65
66         ///
67         Timeout timer;
68         // Multiple Insets can share the same image
69         typedef std::list<Inset const *> InsetList;
70         ///
71         InsetList insets;
72         ///
73         BufferView const * view;
74 };
75
76
77 Loader::Loader()
78         : pimpl_(new Impl(Params()))
79 {}
80
81
82 Loader::Loader(string const & file, DisplayType type)
83         : pimpl_(new Impl(Params()))
84 {
85         reset(file, type);
86 }
87
88
89 Loader::Loader(string const & file, Params const & params)
90         : pimpl_(new Impl(params))
91 {
92         reset(file, params);
93 }
94
95
96 Loader::~Loader()
97 {}
98
99
100 void Loader::reset(string const & file, DisplayType type) const
101 {
102         Params params;
103         params.display = type;
104         pimpl_->resetParams(params);
105
106         pimpl_->resetFile(file);
107         pimpl_->createPixmap();
108 }
109
110
111 void Loader::reset(string const & file, Params const & params) const
112 {
113         pimpl_->resetParams(params);
114         pimpl_->resetFile(file);
115         pimpl_->createPixmap();
116 }
117
118
119 void Loader::reset(Params const & params) const
120 {
121         pimpl_->resetParams(params);
122         pimpl_->createPixmap();
123 }
124
125
126 void Loader::startLoading() const
127 {
128         if (pimpl_->status_ != WaitingToLoad || !pimpl_->cached_item_.get())
129                 return;
130         pimpl_->cached_item_->startLoading();
131 }
132
133
134 void Loader::startLoading(Inset const & inset, BufferView const & bv) const
135 {
136         if (pimpl_->status_ != WaitingToLoad || !pimpl_->cached_item_.get())
137                 return;
138         pimpl_->startLoading(inset, bv);
139 }
140
141
142 void Loader::startMonitoring() const
143 {
144         if (!pimpl_->cached_item_.get())
145                 return;
146
147         pimpl_->cached_item_->startMonitoring();
148 }
149
150
151 bool Loader::monitoring() const
152 {
153         if (!pimpl_->cached_item_.get())
154                 return false;
155
156         return pimpl_->cached_item_->monitoring();
157 }
158
159
160 unsigned long Loader::checksum() const
161 {
162         if (!pimpl_->cached_item_.get())
163                 return 0;
164
165         return pimpl_->cached_item_->checksum();
166 }
167
168
169 string const & Loader::filename() const
170 {
171         static string const empty;
172         return pimpl_->cached_item_.get() ?
173                 pimpl_->cached_item_->filename() : empty;
174 }
175
176
177 ImageStatus Loader::status() const
178 {
179         return pimpl_->status_;
180 }
181
182
183 boost::signals::connection Loader::connect(slot_type const & slot) const
184 {
185         return pimpl_->signal_.connect(slot);
186 }
187
188
189 Image const * Loader::image() const
190 {
191         return pimpl_->image_.get();
192 }
193
194
195 Loader::Impl::Impl(Params const & params)
196         : status_(WaitingToLoad), params_(params),
197           timer(2000, Timeout::ONETIME), view(0)
198 {
199         timer.timeout.connect(boost::bind(&Impl::checkedLoading, this));
200 }
201
202
203 Loader::Impl::~Impl()
204 {
205         resetFile(string());
206 }
207
208
209 void Loader::Impl::resetFile(string const & file)
210 {
211         string const old_file = cached_item_.get() ?
212                 cached_item_->filename() : string();
213
214         if (file == old_file)
215                 return;
216
217         // If monitoring() the current file, should continue to monitor the
218         // new file.
219         bool continue_monitoring = false;
220
221         if (!old_file.empty()) {
222                 continue_monitoring = cached_item_->monitoring();
223                 cached_item_.reset();
224                 Cache::get().remove(old_file);
225         }
226
227         status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
228         image_.reset();
229
230         if (cached_item_.get() || file.empty())
231                 return;
232
233         Cache & gc = Cache::get();
234         if (!gc.inCache(file))
235                 gc.add(file);
236
237         // We /must/ make a local copy of this.
238         cached_item_ = gc.item(file);
239         status_ = cached_item_->status();
240
241         if (continue_monitoring && !cached_item_->monitoring())
242                 cached_item_->startMonitoring();
243
244         cached_item_->connect(boost::bind(&Impl::statusChanged, this));
245 }
246
247
248 void Loader::Impl::resetParams(Params const & params)
249 {
250         if (params == params_)
251                 return;
252
253         params_ = params;
254         status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
255         image_.reset();
256 }
257
258
259 void Loader::Impl::statusChanged()
260 {
261         status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
262         createPixmap();
263         signal_();
264 }
265
266
267 void Loader::Impl::createPixmap()
268 {
269         if (!cached_item_.get() ||
270             params_.display == NoDisplay || status_ != Loaded)
271                 return;
272
273         image_.reset(cached_item_->image()->clone());
274
275         // These do nothing if there's nothing to do
276         image_->clip(params_);
277         image_->rotate(params_);
278         image_->scale(params_);
279
280         bool const success = image_->setPixmap(params_);
281
282         if (success) {
283                 status_ = Ready;
284         } else {
285                 image_.reset();
286                 status_ = ErrorGeneratingPixmap;
287         }
288 }
289
290
291 void Loader::Impl::startLoading(Inset const & inset, BufferView const & bv)
292 {
293         if (status_ != WaitingToLoad || timer.running())
294                 return;
295
296         InsetList::const_iterator it  = insets.begin();
297         InsetList::const_iterator end = insets.end();
298         it = std::find(it, end, &inset);
299         if (it == end)
300                 insets.push_back(&inset);
301         view = &bv;
302
303         timer.start();
304 }
305
306
307 namespace {
308
309 struct FindVisibleInset {
310
311         FindVisibleInset(std::list<VisibleParagraph> const & vps) : vps_(vps) {}
312
313         bool operator()(Inset const * inset_ptr)
314         {
315                 if (!inset_ptr)
316                         return false;
317                 return isInsetVisible(*inset_ptr, vps_);
318         }
319
320 private:
321         std::list<VisibleParagraph> const & vps_;
322 };
323
324 } // namespace anon
325
326
327 void Loader::Impl::checkedLoading()
328 {
329         if (insets.empty() || !view)
330                 return;
331
332         std::list<VisibleParagraph> const vps = getVisibleParagraphs(*view);
333
334         InsetList::const_iterator it  = insets.begin();
335         InsetList::const_iterator end = insets.end();
336
337         it = std::find_if(it, end, FindVisibleInset(vps));
338
339         // One of the insets is visible, so start loading the image.
340         if (it != end)
341                 cached_item_->startLoading();
342 }
343
344
345 } // namespace grfx