/** * \file RenderPreview.cpp * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Angus Leeming * * Full author contact details are available in file CREDITS. */ #include #include "insets/RenderPreview.h" #include "insets/Inset.h" #include "Buffer.h" #include "BufferView.h" #include "Dimension.h" #include "LyX.h" #include "LyXRC.h" #include "MetricsInfo.h" #include "frontends/FontMetrics.h" #include "frontends/Painter.h" #include "graphics/PreviewImage.h" #include "graphics/PreviewLoader.h" #include "support/FileName.h" #include "support/gettext.h" #include "support/lassert.h" #include "support/lstrings.h" using namespace std; using namespace lyx::support; namespace lyx { bool RenderPreview::previewText() { // Use a switch to trigger a warning if the enum is changed. switch(lyxrc.preview) { case LyXRC::PREVIEW_ON: case LyXRC::PREVIEW_NO_MATH: return true; case LyXRC::PREVIEW_OFF: break; } return false; } bool RenderPreview::previewMath() { // Use a switch to trigger a warning if the enum is changed. switch(lyxrc.preview) { case LyXRC::PREVIEW_ON: return true; case LyXRC::PREVIEW_NO_MATH: case LyXRC::PREVIEW_OFF: break; } return false; } RenderPreview::RenderPreview(Inset const * inset) : parent_(inset) {} RenderPreview::RenderPreview(RenderPreview const & other, Inset const * inset) : RenderBase(other), snippet_(other.snippet_), parent_(inset) {} RenderBase * RenderPreview::clone(Inset const * inset) const { return new RenderPreview(*this, inset); } namespace { docstring const statusMessage(BufferView const * bv, string const & snippet) { LASSERT(bv, return docstring()); Buffer const & buffer = bv->buffer(); graphics::PreviewLoader const * loader = buffer.loader(); // please coverity (probably worth the check anyway) if (!loader) return docstring(); graphics::PreviewLoader::Status const status = loader->status(snippet); docstring message; switch (status) { case graphics::PreviewLoader::InQueue: case graphics::PreviewLoader::Processing: message = _("Preview loading"); break; case graphics::PreviewLoader::Ready: message = _("Preview ready"); break; case graphics::PreviewLoader::NotFound: message = _("Preview failed"); break; } return message; } } // namespace graphics::PreviewImage const * RenderPreview::getPreviewImage(Buffer const & buffer) const { graphics::PreviewLoader const * loader = buffer.loader(); LASSERT(loader, return 0); return loader->preview(snippet_); } void RenderPreview::metrics(MetricsInfo & mi, Dimension & dim) const { LBUFERR(mi.base.bv); graphics::PreviewImage const * const pimage = getPreviewImage(mi.base.bv->buffer()); if (pimage) { // If prepared, load the picture before dim calculation. See bug #5627. pimage->image(); dim = pimage->dim(); } else { dim.asc = 50; dim.des = 0; FontInfo font(mi.base.font); font.setFamily(SANS_FAMILY); font.setSize(FOOTNOTE_SIZE); docstring const stat = statusMessage(mi.base.bv, snippet_); dim.wid = 15 + theFontMetrics(font).width(stat); } dim_ = dim; } void RenderPreview::draw(PainterInfo & pi, int x, int y) const { LBUFERR(pi.base.bv); graphics::PreviewImage const * const pimage = getPreviewImage(pi.base.bv->buffer()); graphics::Image const * const image = pimage ? pimage->image() : 0; if (image) { pi.pain.image(x, y - dim_.asc, dim_.wid, dim_.height(), *image); } else { int const offset = Inset::textOffset(pi.base.bv); pi.pain.rectangle(x + offset, y - dim_.asc, dim_.wid - 2 * offset, dim_.asc + dim_.des, Color_foreground); FontInfo font(pi.base.font); font.setFamily(SANS_FAMILY); font.setSize(FOOTNOTE_SIZE); docstring const stat = statusMessage(pi.base.bv, snippet_); pi.pain.text(x + offset + 6, y - theFontMetrics(font).maxAscent() - 4, stat, font); } pi.change.paintCue(pi, x, y - dim_.asc, x + dim_.width(), y - dim_.asc + dim_.height()); } void RenderPreview::startLoading(Buffer const & buffer, bool forexport) const { if (!forexport && (lyxrc.preview == LyXRC::PREVIEW_OFF || snippet_.empty())) return; graphics::PreviewLoader * loader = buffer.loader(); LASSERT(loader, return); loader->startLoading(forexport); } void RenderPreview::addPreview(docstring const & latex_snippet, Buffer const & buffer, bool ignore_lyxrc) { if (lyxrc.preview == LyXRC::PREVIEW_OFF && !ignore_lyxrc) return; graphics::PreviewLoader * loader = buffer.loader(); LASSERT(loader, return); addPreview(latex_snippet, *loader, ignore_lyxrc); } void RenderPreview::addPreview(docstring const & latex_snippet, graphics::PreviewLoader & ploader, bool ignore_lyxrc) { if (lyxrc.preview == LyXRC::PREVIEW_OFF && !ignore_lyxrc) return; // FIXME UNICODE // We have to make sure that we call latex with the right encoding snippet_ = support::trim(to_utf8(latex_snippet)); if (snippet_.empty()) return; if (ploader.preview(snippet_)) return; // If this is the first time of calling, connect to the // PreviewLoader signal that'll inform us when the preview image // is ready for loading. if (!ploader_connection_.connected()) // This is a scoped connection. ploader_connection_ = ploader.connect([this](graphics::PreviewImage const & pi){ imageReady(pi); }); ploader.add(snippet_); } void RenderPreview::removePreview(Buffer const & buffer) { if (snippet_.empty()) return; graphics::PreviewLoader * loader = buffer.loader(); LASSERT(loader, return); loader->remove(snippet_); snippet_.erase(); } void RenderPreview::imageReady(graphics::PreviewImage const & pimage) { // Check the current snippet is the same as that previewed. if (snippet_ == pimage.snippet()) parent_->updateFrontend(); } RenderMonitoredPreview::RenderMonitoredPreview(Inset const * inset) : RenderPreview(inset) { setAbsFile(FileName()); } void RenderMonitoredPreview::setAbsFile(FileName const & file) { bool mon = monitoring(); if (mon) stopMonitoring(); filename_ = file; if (mon) startMonitoring(); } void RenderMonitoredPreview::draw(PainterInfo & pi, int x, int y) const { RenderPreview::draw(pi, x, y); startMonitoring(); monitor_->checkModifiedAsync(); } signals2::connection RenderMonitoredPreview::connect(slot const & slot) { return changed_.connect(slot); } bool RenderMonitoredPreview::monitoring() const { return (bool) monitor_; } void RenderMonitoredPreview::startMonitoring() const { if (!monitoring()) { monitor_ = FileSystemWatcher::activeMonitor(filename_); // Disconnected at the same time as this is destroyed. monitor_->connect([this](bool /* exists */){ changed_(); }); } } void RenderMonitoredPreview::stopMonitoring() const { monitor_ = nullptr; } } // namespace lyx