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