-/*
+/**
* \file GraphicsLoader.C
- * Copyright 2002 the LyX Team
* Read the file COPYING
*
- * \author Angus Leeming <leeming@lyx.org>
+ * \author Angus Leeming
+ *
+ * Full author contact details available in file CREDITS
*/
#include <config.h>
#endif
#include "GraphicsLoader.h"
+
+#include "BufferView.h"
+
#include "GraphicsCache.h"
#include "GraphicsCacheItem.h"
#include "GraphicsImage.h"
#include "GraphicsParams.h"
+#include "GraphicsSupport.h"
+#include "frontends/LyXView.h"
+#include "frontends/Timeout.h"
+
+#include <boost/weak_ptr.hpp>
#include <boost/bind.hpp>
+#include <boost/signals/trackable.hpp>
+
+#include <list>
namespace grfx {
-struct Loader::Impl {
+struct Loader::Impl : boost::signals::trackable {
///
- Impl(Loader &, GParams const &);
+ Impl(Params const &);
///
~Impl();
///
- void setFile(string const & file);
+ void resetFile(string const &);
///
- void unsetOldFile();
+ void resetParams(Params const &);
///
void createPixmap();
- ///
- void statusChanged();
///
- Loader & parent_;
+ void startLoading(Inset const &, BufferView const &);
+
/// The loading status of the image.
ImageStatus status_;
/** Must store a copy of the cached item to ensure that it is not
* erased unexpectedly by the cache itself.
*/
- GraphicPtr graphic_;
- ///
- GParams params_;
+ Cache::ItemPtr cached_item_;
/// We modify a local copy of the image once it is loaded.
- ImagePtr image_;
+ Image::ImagePtr image_;
+ /// This signal is emitted when the image loading status changes.
+ boost::signal0<void> signal_;
+
+private:
+ ///
+ void statusChanged();
+ ///
+ void checkedLoading();
+
+ ///
+ Params params_;
+
+ ///
+ Timeout timer;
+ // Multiple Insets can share the same image
+ typedef std::list<Inset const *> InsetList;
+ ///
+ InsetList insets;
+ ///
+ boost::weak_ptr<BufferView const> view;
};
-Loader::Impl::Impl(Loader & parent, GParams const & params)
- : parent_(parent), status_(WaitingToLoad), params_(params)
+Loader::Loader()
+ : pimpl_(new Impl(Params()))
{}
-Loader::Impl::~Impl()
+Loader::Loader(string const & file, DisplayType type)
+ : pimpl_(new Impl(Params()))
{
- unsetOldFile();
+ reset(file, type);
}
-void Loader::Impl::setFile(string const & file)
+Loader::Loader(string const & file, Params const & params)
+ : pimpl_(new Impl(params))
{
- if (file.empty())
- return;
+ reset(file, params);
+}
- GCache & gc = GCache::get();
- if (!gc.inCache(file))
- gc.add(file);
- // We /must/ make a local copy of this.
- graphic_ = gc.graphic(file);
- status_ = graphic_->status();
+Loader::~Loader()
+{}
- if (status_ == Loaded) {
- createPixmap();
- }
- // It's easiest to do this without checking
- parent_.statusChanged();
+void Loader::reset(string const & file, DisplayType type) const
+{
+ Params params;
+ params.display = type;
+ pimpl_->resetParams(params);
+
+ pimpl_->resetFile(file);
+ pimpl_->createPixmap();
}
-void Loader::Impl::unsetOldFile()
+void Loader::reset(string const & file, Params const & params) const
{
- if (!graphic_.get())
- return;
+ pimpl_->resetParams(params);
+ pimpl_->resetFile(file);
+ pimpl_->createPixmap();
+}
- string const old_file = graphic_->filename();
- graphic_.reset();
- GCache::get().remove(old_file);
- status_ = WaitingToLoad;
- params_ = GParams();
- image_.reset();
+void Loader::reset(Params const & params) const
+{
+ pimpl_->resetParams(params);
+ pimpl_->createPixmap();
}
-void Loader::Impl::statusChanged()
+void Loader::startLoading() const
{
- status_ = graphic_->status();
- if (status_ == Loaded)
- createPixmap();
+ if (pimpl_->status_ != WaitingToLoad || !pimpl_->cached_item_.get())
+ return;
+ pimpl_->cached_item_->startLoading();
+}
+
- parent_.statusChanged();
+void Loader::startLoading(Inset const & inset, BufferView const & bv) const
+{
+ if (pimpl_->status_ != WaitingToLoad || !pimpl_->cached_item_.get())
+ return;
+ pimpl_->startLoading(inset, bv);
}
-void Loader::Impl::createPixmap()
+void Loader::startMonitoring() const
{
- if (!graphic_.get() || image_.get() ||
- params_.display == NoDisplay || status_ != Loaded)
+ if (!pimpl_->cached_item_.get())
return;
- image_.reset(graphic_->image()->clone());
+ pimpl_->cached_item_->startMonitoring();
+}
- // These do nothing if there's nothing to do
- image_->clip(params_);
- image_->rotate(params_);
- image_->scale(params_);
- bool const success = image_->setPixmap(params_);
+bool Loader::monitoring() const
+{
+ if (!pimpl_->cached_item_.get())
+ return false;
- if (success) {
- status_ = Ready;
- } else {
- image_.reset();
- status_ = ErrorGeneratingPixmap;
- }
+ return pimpl_->cached_item_->monitoring();
}
-Loader::Loader()
- : pimpl_(new Impl(*this, GParams()))
-{}
+unsigned long Loader::checksum() const
+{
+ if (!pimpl_->cached_item_.get())
+ return 0;
+ return pimpl_->cached_item_->checksum();
+}
-Loader::Loader(string const & file, DisplayType type)
- : pimpl_(new Impl(*this, GParams()))
+
+string const & Loader::filename() const
{
- pimpl_->params_.display = type;
- pimpl_->setFile(file);
+ static string const empty;
+ return pimpl_->cached_item_.get() ?
+ pimpl_->cached_item_->filename() : empty;
}
-Loader::Loader(string const & file, GParams const & params)
- : pimpl_(new Impl(*this, params))
+ImageStatus Loader::status() const
{
- pimpl_->setFile(file);
+ return pimpl_->status_;
}
-Loader::~Loader()
-{}
+boost::signals::connection Loader::connect(slot_type const & slot) const
+{
+ return pimpl_->signal_.connect(slot);
+}
-void Loader::reset(string const & file, DisplayType type)
+Image const * Loader::image() const
{
- pimpl_->unsetOldFile();
-
- pimpl_->params_ = GParams();
- pimpl_->params_.display = type;
- pimpl_->setFile(file);
+ return pimpl_->image_.get();
}
-void Loader::reset(string const & file, GParams const & params)
+Loader::Impl::Impl(Params const & params)
+ : status_(WaitingToLoad), params_(params),
+ timer(2000, Timeout::ONETIME)
{
- pimpl_->unsetOldFile();
+ timer.timeout.connect(boost::bind(&Impl::checkedLoading, this));
+}
+
- pimpl_->params_ = params;
- pimpl_->setFile(file);
+Loader::Impl::~Impl()
+{
+ resetFile(string());
}
-void Loader::reset(GParams const & params)
+void Loader::Impl::resetFile(string const & file)
{
- pimpl_->params_ = params;
+ string const old_file = cached_item_.get() ?
+ cached_item_->filename() : string();
+
+ if (file == old_file)
+ return;
- if (pimpl_->status_ == Loaded)
- pimpl_->createPixmap();
+ // If monitoring() the current file, should continue to monitor the
+ // new file.
+ bool continue_monitoring = false;
+
+ if (!old_file.empty()) {
+ continue_monitoring = cached_item_->monitoring();
+ cached_item_.reset();
+ Cache::get().remove(old_file);
+ }
+
+ status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
+ image_.reset();
+
+ if (cached_item_.get() || file.empty())
+ return;
+
+ Cache & gc = Cache::get();
+ if (!gc.inCache(file))
+ gc.add(file);
+
+ // We /must/ make a local copy of this.
+ cached_item_ = gc.item(file);
+ status_ = cached_item_->status();
+
+ if (continue_monitoring && !cached_item_->monitoring())
+ cached_item_->startMonitoring();
+
+ cached_item_->connect(boost::bind(&Impl::statusChanged, this));
}
-void Loader::startLoading()
+void Loader::Impl::resetParams(Params const & params)
{
- if (pimpl_->status_ != WaitingToLoad || !pimpl_->graphic_.get())
+ if (params == params_)
return;
- pimpl_->graphic_->statusChanged.connect(
- boost::bind(&Loader::Impl::statusChanged,
- pimpl_.get()));
- pimpl_->graphic_->startLoading();
+ params_ = params;
+ status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
+ image_.reset();
}
-string const & Loader::filename() const
+void Loader::Impl::statusChanged()
{
- static string const empty;
- return pimpl_->graphic_.get() ? pimpl_->graphic_->filename() : empty;
+ status_ = cached_item_.get() ? cached_item_->status() : WaitingToLoad;
+ createPixmap();
+ signal_();
}
-ImageStatus Loader::status() const
+void Loader::Impl::createPixmap()
{
- return pimpl_->status_;
+ if (!cached_item_.get() ||
+ params_.display == NoDisplay || status_ != Loaded)
+ return;
+
+ image_.reset(cached_item_->image()->clone());
+
+ // These do nothing if there's nothing to do
+ image_->clip(params_);
+ image_->rotate(params_);
+ image_->scale(params_);
+
+ bool const success = image_->setPixmap(params_);
+
+ if (success) {
+ status_ = Ready;
+ } else {
+ image_.reset();
+ status_ = ErrorGeneratingPixmap;
+ }
}
-GImage const * Loader::image() const
+void Loader::Impl::startLoading(Inset const & inset, BufferView const & bv)
{
- return pimpl_->image_.get();
+ if (status_ != WaitingToLoad || timer.running())
+ return;
+
+ InsetList::const_iterator it = insets.begin();
+ InsetList::const_iterator end = insets.end();
+ it = std::find(it, end, &inset);
+ if (it == end)
+ insets.push_back(&inset);
+ view = bv.owner()->view();
+
+ timer.start();
+}
+
+
+namespace {
+
+struct FindVisibleInset {
+
+ FindVisibleInset(std::list<VisibleParagraph> const & vps) : vps_(vps) {}
+
+ bool operator()(Inset const * inset_ptr)
+ {
+ if (!inset_ptr)
+ return false;
+ return isInsetVisible(*inset_ptr, vps_);
+ }
+
+private:
+ std::list<VisibleParagraph> const & vps_;
+};
+
+} // namespace anon
+
+
+void Loader::Impl::checkedLoading()
+{
+ if (insets.empty() || !view.get())
+ return;
+
+ std::list<VisibleParagraph> const vps =
+ getVisibleParagraphs(*view.get());
+
+ InsetList::const_iterator it = insets.begin();
+ InsetList::const_iterator end = insets.end();
+
+ it = std::find_if(it, end, FindVisibleInset(vps));
+
+ // One of the insets is visible, so start loading the image.
+ if (it != end)
+ cached_item_->startLoading();
}
+
} // namespace grfx