From f68a2bfb915bf590b9dd11061750b7922e5c1deb Mon Sep 17 00:00:00 2001 From: Angus Leeming Date: Fri, 5 Jul 2002 21:24:15 +0000 Subject: [PATCH] Preview code git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@4538 a592a061-630c-0410-9148-cb99ea01b6c8 --- lib/ChangeLog | 5 + lib/scripts/lyxpreview2ppm.sh | 111 ++++++++ lib/scripts/lyxpreview2xpm.sh | 15 - src/BufferView_pimpl.C | 5 + src/ChangeLog | 11 + src/LColor.C | 1 + src/LColor.h | 2 + src/buffer.C | 5 + src/frontends/ChangeLog | 4 + src/frontends/lyx_gui.h | 13 +- src/frontends/xforms/ChangeLog | 4 + src/frontends/xforms/lyx_gui.C | 35 ++- src/graphics/ChangeLog | 13 + src/graphics/Makefile.am | 11 +- src/graphics/PreviewImage.C | 156 ++++++++++ src/graphics/PreviewImage.h | 60 ++++ src/graphics/PreviewLoader.C | 506 +++++++++++++++++++++++++++++++++ src/graphics/PreviewLoader.h | 86 ++++++ src/graphics/PreviewMetrics.C | 99 +++++++ src/graphics/PreviewMetrics.h | 43 +++ src/graphics/Previews.C | 109 +++++++ src/graphics/Previews.h | 73 +++++ src/insets/ChangeLog | 4 + src/insets/inset.h | 13 + src/mathed/ChangeLog | 10 + src/mathed/formula.C | 206 ++++++++------ src/mathed/formula.h | 24 +- src/mathed/math_nestinset.C | 18 +- 28 files changed, 1527 insertions(+), 115 deletions(-) create mode 100644 lib/scripts/lyxpreview2ppm.sh delete mode 100755 lib/scripts/lyxpreview2xpm.sh create mode 100644 src/graphics/PreviewImage.C create mode 100644 src/graphics/PreviewImage.h create mode 100644 src/graphics/PreviewLoader.C create mode 100644 src/graphics/PreviewLoader.h create mode 100644 src/graphics/PreviewMetrics.C create mode 100644 src/graphics/PreviewMetrics.h create mode 100644 src/graphics/Previews.C create mode 100644 src/graphics/Previews.h diff --git a/lib/ChangeLog b/lib/ChangeLog index 7820c86d9f..3a91d72f3e 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,8 @@ +2002-07-05 Angus Leeming + + * scripts/lyxpreview2ppm.sh: added. + * scripts/lyxpreview2xpm: removed. + 2002-06-19 Herbert Voss * configure.m4: add converters for epsi and (x)fig diff --git a/lib/scripts/lyxpreview2ppm.sh b/lib/scripts/lyxpreview2ppm.sh new file mode 100644 index 0000000000..e6622432e8 --- /dev/null +++ b/lib/scripts/lyxpreview2ppm.sh @@ -0,0 +1,111 @@ +#!/bin/sh + +# This script takes a LaTeX file and generates PPM files, one per page. +# The idea is to use it with preview.sty to create small bitmap previews of +# things like math equations. + +# The script takes two arguments, the name of the file to be converted and +# the resolution of the generated image, to be passed to gs. +if [ $# -ne 2 ]; then + exit 1 +fi + +# A couple of helper functions +FIND_EXECUTABLE="" +FIND_IT () { + which ${FIND_EXECUTABLE} > /dev/null + STATUS=$? + if [ ${STATUS} -ne 0 ]; then + echo "Unable to find \"${FIND_EXECUTABLE}\". Please install." + exit 1 + fi +} + +CHECK_STATUS () { + if [ ${STATUS} -ne 0 ]; then + echo "${EXECUTABLE} failed." + # Remove everything except the original .tex file. + FILES=`ls ${BASE}* | sed -e "/${BASE}.tex/d"` + rm -f ${FILES} + exit ${STATUS} + fi +} + +# We use latex, dvips and gs, so check that they're all there. +FIND_EXECUTABLE=latex; FIND_IT +FIND_EXECUTABLE=dvips; FIND_IT +FIND_EXECUTABLE=gs; FIND_IT + +# Initialise some variables. +TEXFILE=$1 +RESOLUTION=$2 + +DIR=`dirname ${TEXFILE}` +BASE=`basename ${TEXFILE} .tex` +DVIFILE=${BASE}.dvi +PSFILE=${BASE}.ps +METRICS=${BASE}.metrics + +# Perform the conversion. +cd ${DIR} +latex -interaction=batchmode ${TEXFILE} + +STATUS=$? +EXECUTABLE="latex ${TEXFILE}"; CHECK_STATUS + +dvips -o ${PSFILE} ${DVIFILE} + +STATUS=$? +EXECUTABLE="dvips ${DVIFILE}"; CHECK_STATUS + +# Older versions of gs have problems with a large degree of anti-aliasing +# at high resolutions +ALPHA=4 +if [ ${RESOLUTION} -gt 150 ]; then + ALPHA=2 +fi + +gs -q -dNOPAUSE -dBATCH -dSAFER -sDEVICE=pnm -sOutputFile=${BASE}%03d.ppm \ + -dGraphicsAlphaBit=${ALPHA} -dTextAlphaBits=${ALPHA} -r${RESOLUTION} \ + ${PSFILE} + +STATUS=$? +EXECUTABLE="gs ${PSFILE}"; CHECK_STATUS + +# Attempt to generate a file ${METRICS} that contains only the tightpage +# bounding box info, extract from ${PSFILE} + +# 1. Create a file containing the sed instructions +SEDSCRIPT=bbox.sed +cat - > ${SEDSCRIPT} < ${METRICS} +STATUS=$? +rm -f ${SEDSCRIPT} +EXECUTABLE="extracting metrics"; CHECK_STATUS + +# All was successful, so remove everything except the ppm files and the +# metrics file. +FILES=`ls ${BASE}* | sed -e "/${BASE}.metrics/d" -e "/${BASE}.*.ppm/d"` +rm -f ${FILES} diff --git a/lib/scripts/lyxpreview2xpm.sh b/lib/scripts/lyxpreview2xpm.sh deleted file mode 100755 index f7a25e4d37..0000000000 --- a/lib/scripts/lyxpreview2xpm.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# converts a single inset's TeX representation to an .xpm - -dir=`dirname $2` -base=${dir}/`basename $2 .xpm` - -cp $1 ${base}.tex -#(cd ${dir} ; latex ${base}) -(cd ${dir} ; pdflatex --interaction batchmode ${base}) -dvips -x 1750 -R -o ${base}.ps ${base}.dvi -convert -crop 0x0 ${base}.ps ${base}.xpm -rm ${base}.tex ${base}.aux ${base}.dvi ${base}.log ${base}.eps || true - -exit 0 diff --git a/src/BufferView_pimpl.C b/src/BufferView_pimpl.C index 3268e5cc26..ac2f6b663e 100644 --- a/src/BufferView_pimpl.C +++ b/src/BufferView_pimpl.C @@ -70,6 +70,8 @@ #include "mathed/formulabase.h" +#include "graphics/Previews.h" + #include "support/LAssert.h" #include "support/lstrings.h" #include "support/filetools.h" @@ -238,6 +240,9 @@ void BufferView::Pimpl::buffer(Buffer * b) owner_->updateToolbar(); owner_->updateLayoutChoice(); owner_->updateWindowTitle(); + + if (grfx::Previews::activated() && buffer_) + grfx::Previews::get().generateBufferPreviews(buffer_); } diff --git a/src/ChangeLog b/src/ChangeLog index 92dff26585..cfe063f05e 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,14 @@ +2002-07-05 Angus Leeming + + * BufferView_pimpl.C (buffer): generate previews if desired. + + * LColor.h: add "preview" to the color enum. + + * LColor.C (LColor): add a corresponding entry to the items array. + + * buffer.C (~Buffer): remove any previewed LaTeX snippets assocoated + with this buffer. + 2002-07-05 Angus Leeming * buffer.[Ch] (makeLaTeXFile): create two methods where there was one. diff --git a/src/LColor.C b/src/LColor.C index e1fe8c2d1f..44e3004dac 100644 --- a/src/LColor.C +++ b/src/LColor.C @@ -60,6 +60,7 @@ LColor::LColor() { foreground, N_("text"), "foreground", "black", "foreground" }, { selection, N_("selection"), "selection", "LightBlue", "selection" }, { latex, N_("latex text"), "latex", "DarkRed", "latex" }, + { preview, N_("previewed snippet"), "preview", "black", "preview" }, { note, N_("note"), "note", "yellow", "note" }, { notebg, N_("note background"), "notebg", "yellow", "notebg" }, { depthbar, N_("depth bar"), "depthbar", "IndianRed", "depthbar" }, diff --git a/src/LColor.h b/src/LColor.h index b41608a0a7..da75feef92 100644 --- a/src/LColor.h +++ b/src/LColor.h @@ -77,6 +77,8 @@ public: selection, /// Text color in LaTeX mode latex, + /// The color used for previews + preview, /// Text color for notes note, diff --git a/src/buffer.C b/src/buffer.C index a1563139d1..d51ac5c27a 100644 --- a/src/buffer.C +++ b/src/buffer.C @@ -83,6 +83,8 @@ #include "frontends/Dialogs.h" #include "frontends/Alert.h" +#include "graphics/Previews.h" + #include "support/textutils.h" #include "support/filetools.h" #include "support/path.h" @@ -194,6 +196,9 @@ Buffer::~Buffer() par = tmppar; } paragraph = 0; + + // Remove any previewed LaTeX snippets assocoated with this buffer. + grfx::Previews::get().removeLoader(this); } diff --git a/src/frontends/ChangeLog b/src/frontends/ChangeLog index b911b35cc6..816c96626c 100644 --- a/src/frontends/ChangeLog +++ b/src/frontends/ChangeLog @@ -1,3 +1,7 @@ +2002-07-05 Angus Leeming + + * lyx_gui.h (hexname): new function. + 2002-07-04 Lars Gullik Bjønnes * screen.C (SplashScreen): we change diff --git a/src/frontends/lyx_gui.h b/src/frontends/lyx_gui.h index 83b674d90e..98f68cd878 100644 --- a/src/frontends/lyx_gui.h +++ b/src/frontends/lyx_gui.h @@ -9,11 +9,9 @@ #ifndef LYX_GUI_H #define LYX_GUI_H - -#include - -#include "LString.h" - + +#include "LColor.h" +#include "LString.h" #include class Dialogs; @@ -38,6 +36,11 @@ namespace lyx_gui { /// initialise graphics void init_graphics(); + + /** Eg, passing LColor::black returns "000000", + * passing LColor::white returns "ffffff". + */ + string const hexname(LColor::color col); } #endif // LYX_GUI_H diff --git a/src/frontends/xforms/ChangeLog b/src/frontends/xforms/ChangeLog index d173b53e3d..e5b2f71545 100644 --- a/src/frontends/xforms/ChangeLog +++ b/src/frontends/xforms/ChangeLog @@ -1,3 +1,7 @@ +2002-07-05 Angus Leeming + + * lyx_gui.C (hexname): new function. + 2002-07-04 Lars Gullik Bjønnes * lyx_gui.C (init_graphics): boost::function assign, not diff --git a/src/frontends/xforms/lyx_gui.C b/src/frontends/xforms/lyx_gui.C index 207a33bcc8..bf859d2c7e 100644 --- a/src/frontends/xforms/lyx_gui.C +++ b/src/frontends/xforms/lyx_gui.C @@ -37,9 +37,9 @@ #include "graphics/GraphicsImageXPM.h" #endif - +#include "Lsstream.h" +#include #include - #include #ifndef CXX_GLOBAL_CSTD @@ -49,6 +49,9 @@ using std::exit; using std::vector; using std::hex; using std::endl; +using std::setbase; +using std::setfill; +using std::setw; extern bool finished; extern BufferList bufferlist; @@ -308,3 +311,31 @@ void lyx_gui::init_graphics() Image::loadableFormats = boost::bind(&ImageXPM::loadableFormats); #endif } + + +string const lyx_gui::hexname(LColor::color col) +{ + string const name = lcolor.getX11Name(col); + Display * const display = fl_get_display(); + Colormap const cmap = fl_state[fl_get_vclass()].colormap; + XColor xcol, ccol; + + if (XLookupColor(display, cmap, name.c_str(), &xcol, &ccol) == 0) { + lyxerr << "X can't find color \"" + << lcolor.getLyXName(col) + << "\"" << endl; + return string(); + } + + ostringstream os; + + // Note that X stores the RGB values in the range 0 - 65535 + // whilst we require them in the range 0 - 255. + os << setbase(16) << setfill('0') + << setw(2) << (xcol.red / 256) + << setw(2) << (xcol.green / 256) + << setw(2) << (xcol.blue / 256); + + return os.str().c_str(); +} + diff --git a/src/graphics/ChangeLog b/src/graphics/ChangeLog index 48ac83f9c1..4210985328 100644 --- a/src/graphics/ChangeLog +++ b/src/graphics/ChangeLog @@ -1,3 +1,16 @@ +2002-07-05 Angus Leeming + + * PreviewImage.h: + * PreviewImage.C: + * PreviewLoader.h: + * PreviewLoader.C: + * PreviewMetrics.h: + * PreviewMetrics.C: + * Previews.h: + * Previews.C: new files. The previewed LaTeX snippet stuff. + + * Makefile.am: add these files. + 2002-07-05 Angus Leeming * GraphicsLoader.h: whitespace. diff --git a/src/graphics/Makefile.am b/src/graphics/Makefile.am index e000750075..22ff5535be 100644 --- a/src/graphics/Makefile.am +++ b/src/graphics/Makefile.am @@ -23,4 +23,13 @@ libgraphics_la_SOURCES = \ GraphicsLoader.C \ $(GRAPHICSIMAGEXPM) GraphicsParams.C \ GraphicsParams.h \ - GraphicsTypes.h + GraphicsTypes.h \ + PreviewImage.h \ + PreviewImage.C \ + PreviewLoader.h \ + PreviewLoader.C \ + PreviewMetrics.h \ + PreviewMetrics.C \ + Previews.h \ + Previews.C + diff --git a/src/graphics/PreviewImage.C b/src/graphics/PreviewImage.C new file mode 100644 index 0000000000..8f90b20379 --- /dev/null +++ b/src/graphics/PreviewImage.C @@ -0,0 +1,156 @@ +/** + * \file PreviewImage.C + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author Angus Leeming + */ + +#include + +#ifdef __GNUG__ +#pragma implementation +#endif + +#include "PreviewImage.h" +#include "PreviewLoader.h" +#include "GraphicsImage.h" +#include "GraphicsLoader.h" + +#include "debug.h" + +#include "support/lyxlib.h" + +#include +#include + + +namespace grfx { + +struct PreviewImage::Impl : public boost::signals::trackable { + /// + Impl(PreviewImage & p, PreviewLoader & l, + string const & s, string const & f, double af); + /// + void startLoading(); + /// + Image const * image() const { return iloader_->image(); } + /// + void statusChanged(); + + /// + PreviewImage const & parent_; + /// + PreviewLoader & ploader_; + /// + boost::scoped_ptr const iloader_; + /// + string const snippet_; + /// + double const ascent_frac_; +}; + + +PreviewImage::PreviewImage(PreviewLoader & l, + string const & s, + string const & f, + double af) + : pimpl_(new Impl(*this, l, s, f, af)) +{} + + +PreviewImage::~PreviewImage() +{} + + +void PreviewImage::startLoading() +{ + return pimpl_->startLoading(); +} + + +string const & PreviewImage::snippet() const +{ + return pimpl_->snippet_; +} + +int PreviewImage::ascent() const +{ + Image const * const image = pimpl_->image(); + if (!image) + return 0; + + return int(pimpl_->ascent_frac_ * double(image->getHeight())); +} + + +int PreviewImage::descent() const +{ + Image const * const image = pimpl_->image(); + if (!image) + return 0; + + return int((1.0 - pimpl_->ascent_frac_) * double(image->getHeight())); +} + + +int PreviewImage::width() const +{ + Image const * const image = pimpl_->image(); + return image ? image->getWidth() : 0; +} + + +Image const * PreviewImage::image() const +{ + return pimpl_->image(); +} + + +PreviewImage::Impl::Impl(PreviewImage & p, PreviewLoader & l, + string const & s, + string const & bf, + double af) + : parent_(p), ploader_(l), iloader_(new Loader(bf)), + snippet_(s), ascent_frac_(af) +{} + + +void PreviewImage::Impl::startLoading() +{ + if (iloader_->status() != WaitingToLoad) + return; + + iloader_->statusChanged.connect( + boost::bind(&Impl::statusChanged, this)); + iloader_->startLoading(); +} + + +void PreviewImage::Impl::statusChanged() +{ + switch (iloader_->status()) { + case WaitingToLoad: + case Loading: + case Converting: + case Loaded: + case ScalingEtc: + break; + + case ErrorNoFile: + case ErrorConverting: + case ErrorLoading: + case ErrorGeneratingPixmap: + case ErrorUnknown: + //lyx::unlink(iloader_->filename()); + ploader_.remove(snippet_); + break; + + case Ready: + lyx::unlink(iloader_->filename()); + ploader_.imageReady(parent_); + break; + } +} + +} // namespace grfx diff --git a/src/graphics/PreviewImage.h b/src/graphics/PreviewImage.h new file mode 100644 index 0000000000..67b67ed856 --- /dev/null +++ b/src/graphics/PreviewImage.h @@ -0,0 +1,60 @@ +// -*- C++ -*- +/** + * \file PreviewImage.h + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author Angus Leeming + */ + +#ifndef PREVIEWIMAGE_H +#define PREVIEWIMAGE_H + +#ifdef __GNUG__ +#pragma interface +#endif + +#include "LString.h" +#include + +namespace grfx { + +class PreviewLoader; +class Image; + +class PreviewImage { +public: + /** ascent = height * ascent_frac + * descent = height * (1 - ascent_frac) + */ + PreviewImage(PreviewLoader & parent, + string const & latex_snippet, + string const & bitmap_file, + double ascent_frac); + /// + ~PreviewImage(); + + /// We are explicit about when we begin the loading process. + void startLoading(); + + /// + string const & snippet() const; + /// + int ascent() const; + /// + int descent() const; + /// + int width() const; + /// + Image const * image() const; + +private: + /// Use the Pimpl idiom to hide the internals. + class Impl; + /// The pointer never changes although *pimpl_'s contents may. + boost::scoped_ptr const pimpl_; +}; + +} // namespace grfx + +#endif // PREVIEWIMAGE_H diff --git a/src/graphics/PreviewLoader.C b/src/graphics/PreviewLoader.C new file mode 100644 index 0000000000..6a18ebd988 --- /dev/null +++ b/src/graphics/PreviewLoader.C @@ -0,0 +1,506 @@ +/* + * \file PreviewLoader.C + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author Angus Leeming + */ + +#include + +#ifdef __GNUG__ +#pragma implementation +#endif + +#include "PreviewLoader.h" +#include "PreviewImage.h" +#include "PreviewMetrics.h" + +#include "buffer.h" +#include "bufferparams.h" +#include "converter.h" +#include "debug.h" +#include "lyxrc.h" +#include "LColor.h" + +#include "insets/inset.h" + +#include "frontends/lyx_gui.h" // hexname + +#include "support/filetools.h" +#include "support/forkedcall.h" +#include "support/lstrings.h" +#include "support/lyxlib.h" + +#include +#include + +#include +#include +#include + +using std::endl; +using std::ofstream; +using std::ostream; +using std::setfill; +using std::setw; + + +namespace { + +string const unique_filename() +{ + + static string dir; + if (dir.empty()) { + string const tmp = lyx::tempName(); + lyx::unlink(tmp); + dir = OnlyPath(tmp); + } + + static int theCounter = 0; + ostringstream os; + os << dir << theCounter++ << "lyxpreview"; + + return os.str().c_str(); +} + +} // namespace anon + + +namespace grfx { + +struct PreviewLoader::Impl : public boost::signals::trackable { + /// + Impl(PreviewLoader & p, Buffer const & b); + /// + PreviewImage const * preview(string const & latex_snippet) const; + /// + PreviewLoader::Status status(string const & latex_snippet) const; + /// + void add(string const & latex_snippet); + /// + void remove(string const & latex_snippet); + /// + void startLoading(); + +private: + /// Called by the Forkedcall process that generated the bitmap files. + void finishedGenerating(string const &, pid_t, int); + /// + void dumpPreamble(ostream &) const; + /// + void dumpData(ostream &) const; + + /// + static void setConverter(); + /// We don't own this + static Converter const * pconverter_; + + /** The cache allows easy retrieval of already-generated images + * using the LaTeX snippet as the identifier. + */ + typedef boost::shared_ptr PreviewImagePtr; + /// + typedef std::map Cache; + /// + Cache cache_; + + /** The map stores the LaTeX snippet and the name of the generated + * bitmap image file. + */ + typedef std::map PendingMap; + /// + PendingMap pending_; + + /// Store info on a currently executing, forked process. + struct InProgress { + /// + typedef std::map PendingMap; + /// + InProgress() {} + /// + InProgress(string const & mf, PendingMap const & s) + : metrics_file(mf), snippets(s) + {} + /// + string metrics_file; + /// + PendingMap snippets; + }; + friend class InProgress; + + /// Store all forked processes so that we can proceed thereafter. + typedef std::map InProgressMap; + /// + InProgressMap in_progress_; + + /// + string filename_base_; + /// + PreviewLoader & parent_; + /// + Buffer const & buffer_; +}; + + +Converter const * PreviewLoader::Impl::pconverter_; + + +PreviewLoader::PreviewLoader(Buffer const & b) + : pimpl_(new Impl(*this, b)) +{} + + +PreviewLoader::~PreviewLoader() +{} + + +PreviewImage const * PreviewLoader::preview(string const & latex_snippet) const +{ + return pimpl_->preview(latex_snippet); +} + + +PreviewLoader::Status PreviewLoader::status(string const & latex_snippet) const +{ + return pimpl_->status(latex_snippet); +} + + +void PreviewLoader::add(string const & latex_snippet) +{ + pimpl_->add(latex_snippet); +} + + +void PreviewLoader::remove(string const & latex_snippet) +{ + pimpl_->remove(latex_snippet); +} + + +void PreviewLoader::startLoading() +{ + pimpl_->startLoading(); +} + + +void PreviewLoader::Impl::setConverter() +{ + if (pconverter_) + return; + + string const from = "lyxpreview"; + + Formats::FormatList::const_iterator it = formats.begin(); + Formats::FormatList::const_iterator end = formats.end(); + + for (; it != end; ++it) { + string const to = it->name(); + if (from == to) + continue; + Converter const * ptr = converters.getConverter(from, to); + if (ptr) { + pconverter_ = ptr; + break; + } + } + + if (pconverter_) + return; + + static bool first = true; + if (!first) + return; + + first = false; + lyxerr << "PreviewLoader::startLoading()\n" + << "No converter from \"lyxpreview\" format has been defined." + << endl; +} + + +PreviewLoader::Impl::Impl(PreviewLoader & p, Buffer const & b) + : filename_base_(unique_filename()), parent_(p), buffer_(b) +{} + + +PreviewImage const * +PreviewLoader::Impl::preview(string const & latex_snippet) const +{ + Cache::const_iterator it = cache_.find(latex_snippet); + return (it == cache_.end()) ? 0 : it->second.get(); +} + + +PreviewLoader::Status +PreviewLoader::Impl::status(string const & latex_snippet) const +{ + Cache::const_iterator cit = cache_.find(latex_snippet); + if (cit != cache_.end()) + return PreviewLoader::Ready; + + PendingMap::const_iterator pit = pending_.find(latex_snippet); + if (pit != pending_.end()) + return PreviewLoader::InQueue; + + InProgressMap::const_iterator git = in_progress_.begin(); + InProgressMap::const_iterator gend = in_progress_.end(); + + for (; git != gend; ++git) { + PendingMap::const_iterator pit = + git->second.snippets.find(latex_snippet); + if (pit != git->second.snippets.end()) + return PreviewLoader::Processing; + } + + return PreviewLoader::NotFound; +} + + +void PreviewLoader::Impl::add(string const & latex_snippet) +{ + if (!pconverter_) { + setConverter(); + if (!pconverter_) + return; + } + + Cache::const_iterator cit = cache_.find(latex_snippet); + if (cit != cache_.end()) + return; + + PendingMap::const_iterator pit = pending_.find(latex_snippet); + if (pit != pending_.end()) + return; + + int const snippet_counter = int(pending_.size()) + 1; + ostringstream os; + os << filename_base_ + << setfill('0') << setw(3) << snippet_counter + << "." << pconverter_->to; + string const image_filename = os.str().c_str(); + + pending_[latex_snippet] = image_filename; +} + + +void PreviewLoader::Impl::remove(string const & latex_snippet) +{ + Cache::iterator cit = cache_.find(latex_snippet); + if (cit != cache_.end()) + cache_.erase(cit); + + PendingMap::iterator pit = pending_.find(latex_snippet); + if (pit != pending_.end()) + pending_.erase(pit); + + InProgressMap::iterator git = in_progress_.begin(); + InProgressMap::iterator gend = in_progress_.end(); + + while (git != gend) { + InProgressMap::iterator curr = git; + ++git; + + PendingMap::iterator pit = + curr->second.snippets.find(latex_snippet); + if (pit != curr->second.snippets.end()) + curr->second.snippets.erase(pit); + + if (curr->second.snippets.empty()) + in_progress_.erase(curr); + } +} + + +void PreviewLoader::Impl::startLoading() +{ + if (pending_.empty()) + return; + + if (!pconverter_) { + setConverter(); + if (!pconverter_) + return; + } + + lyxerr[Debug::GRAPHICS] << "PreviewLoader::startLoading()" << endl; + + // Output the LaTeX file. + string const latexfile = filename_base_ + ".tex"; + + ofstream of(latexfile.c_str()); + dumpPreamble(of); + of << "\n\\begin{document}\n"; + dumpData(of); + of << "\n\\end{document}\n"; + of.close(); + + // The conversion command. + ostringstream cs; + cs << pconverter_->command << " " << latexfile << " " + << tostr(0.01 * lyxrc.dpi * lyxrc.zoom); + + // Store the generation process in a list of all generating processes + // (I anticipate that this will be small!) + string const command = cs.str().c_str(); + string const metrics_file = filename_base_ + ".metrics"; + in_progress_[command] = InProgress(metrics_file, pending_); + + // Reset the filename and clear the data, so we're ready to + // start afresh. + pending_.clear(); + filename_base_ = unique_filename(); + + // Initiate the conversion from LaTeX to bitmap images files. + Forkedcall::SignalTypePtr convert_ptr; + convert_ptr.reset(new Forkedcall::SignalType); + + convert_ptr->connect( + boost::bind(&Impl::finishedGenerating, this, _1, _2, _3)); + + Forkedcall call; + int ret = call.startscript(command, convert_ptr); + + if (ret != 0) { + InProgressMap::iterator it = in_progress_.find(command); + if (it != in_progress_.end()) + in_progress_.erase(it); + + lyxerr[Debug::GRAPHICS] << "PreviewLoader::startLoading()\n" + << "Unable to start process \n" + << command << endl; + } +} + + +void PreviewLoader::Impl::finishedGenerating(string const & command, + pid_t /* pid */, int retval) +{ + string const status = retval > 0 ? "failed" : "succeeded"; + lyxerr[Debug::GRAPHICS] << "PreviewLoader::finishedInProgress(" + << retval << "): processing " << status + << " for " << command << endl; + if (retval > 0) + return; + + InProgressMap::iterator git = in_progress_.find(command); + if (git == in_progress_.end()) { + lyxerr << "PreviewLoader::finishedGenerating(): unable to find " + "data for\n" + << command << "!" << endl; + return; + } + + // Read the metrics file, if it exists + PreviewMetrics metrics_file(git->second.metrics_file); + + // Add these newly generated bitmap files to the cache and + // start loading them into LyX. + PendingMap::const_iterator it = git->second.snippets.begin(); + PendingMap::const_iterator end = git->second.snippets.end(); + + int metrics_counter = 0; + for (; it != end; ++it) { + string const & snip = it->first; + + // Paranoia check + Cache::const_iterator chk = cache_.find(snip); + if (chk != cache_.end()) + continue; + + // Mental note (Angus, 4 July 2002, having just found out the + // hard way :-(). + // We /must/ first add to the cache and then start the + // image loading process. + // If not, then outside functions can be called before by the + // image loader before the PreviewImage is properly constucted. + // This can lead to all sorts of horribleness if such a + // function attempts to access its internals. + string const & file = it->second; + double af = metrics_file.ascent_fraction(metrics_counter++); + PreviewImagePtr ptr(new PreviewImage(parent_, snip, file, af)); + + cache_[snip] = ptr; + + ptr->startLoading(); + } + + in_progress_.erase(git); +} + + +void PreviewLoader::Impl::dumpPreamble(ostream & os) const +{ + // Why on earth is Buffer::makeLaTeXFile a non-const method? + Buffer & tmp = const_cast(buffer_); + // Dump the preamble only. + tmp.makeLaTeXFile(os, string(), true, false, true); + + // Loop over the insets in the buffer and dump all the math-macros. + Buffer::inset_iterator it = buffer_.inset_const_iterator_begin(); + Buffer::inset_iterator end = buffer_.inset_const_iterator_end(); + + for (; it != end; ++it) { + if ((*it)->lyxCode() == Inset::MATHMACRO_CODE) { + (*it)->latex(&buffer_, os, true, true); + } + } + + // Use the preview style file to ensure that each snippet appears on a + // fresh page. + os << "\n" + << "\\usepackage[active,dvips,tightpage]{preview}\n" + << "\n"; + + // This piece of PostScript magic ensures that the foreground and + // background colors are the same as the LyX screen. + string fg = lyx_gui::hexname(LColor::preview); + if (fg.empty()) fg = "000000"; + + string bg = lyx_gui::hexname(LColor::background); + if (bg.empty()) bg = "ffffff"; + + os << "\\AtBeginDocument{\\AtBeginDvi{%\n" + << "\\special{!userdict begin/bop-hook{//bop-hook exec\n" + << "<" << fg << bg << ">{255 div}forall setrgbcolor\n" + << "clippath fill setrgbcolor}bind def end}}}\n"; +} + + +namespace { + +typedef std::pair StrPair; + +struct CompSecond { + bool operator()(StrPair const & lhs, StrPair const & rhs) + { + return lhs.second < rhs.second; + } +}; + +} // namespace anon + + +void PreviewLoader::Impl::dumpData(ostream & os) const +{ + if (pending_.empty()) + return; + + // Sorting by image filename ensures that the snippets are put into + // the LaTeX file in the expected order. + std::vector vec(pending_.begin(), pending_.end()); + std::sort(vec.begin(), vec.end(), CompSecond()); + + std::vector::const_iterator it = vec.begin(); + std::vector::const_iterator end = vec.end(); + + for (; it != end; ++it) { + os << "\\begin{preview}\n" + << it->first + << "\n\\end{preview}\n\n"; + } +} + +} // namespace grfx diff --git a/src/graphics/PreviewLoader.h b/src/graphics/PreviewLoader.h new file mode 100644 index 0000000000..3ba6e8f36b --- /dev/null +++ b/src/graphics/PreviewLoader.h @@ -0,0 +1,86 @@ +// -*- C++ -*- +/** + * \file PreviewLoader.h + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author Angus Leeming + * + * grfx::PreviewLoader collects latex snippets together. Then, on a + * startLoading() call, these are dumped to file and processed, converting + * each snippet to a separate bitmap image file. Once a bitmap file is ready + * to be loaded back into LyX, the PreviewLoader emits a readyToDisplay signal + * to inform the initiating process. + */ + +#ifndef PREVIEWLOADER_H +#define PREVIEWLOADER_H + +#ifdef __GNUG__ +#pragma interface +#endif + +#include "LString.h" +#include +#include +#include + +class Buffer; + +namespace grfx { + +class PreviewImage; + +class PreviewLoader : boost::noncopyable { +public: + /** We need buffer because we require the preamble to the + * LaTeX file. + */ + PreviewLoader(Buffer const & buffer); + /// + ~PreviewLoader(); + + /** Is there an image already associated with this snippet of LaTeX? + * If so, returns a pointer to it, else returns 0. + */ + PreviewImage const * preview(string const & latex_snippet) const; + + /// + enum Status { + /// + NotFound, + /// + InQueue, + /// + Processing, + /// + Ready + }; + + /// How far have we got in loading the image? + Status status(string const & latex_snippet) const; + + /// Add a snippet of LaTeX to the queue for processing. + void add(string const & latex_snippet); + + /// Remove this snippet of LaTeX from the PreviewLoader. + void remove(string const & latex_snippet); + + /** We have accumulated several latex snippets with status "InQueue". + * Initiate their transformation into bitmap images. + */ + void startLoading(); + + /// Emit this signal when an image is ready for display. + boost::signal1 imageReady; + +private: + /// Use the Pimpl idiom to hide the internals. + class Impl; + /// The pointer never changes although *pimpl_'s contents may. + boost::scoped_ptr const pimpl_; +}; + +} // namespace grfx + +#endif // PREVIEWLOADER_H diff --git a/src/graphics/PreviewMetrics.C b/src/graphics/PreviewMetrics.C new file mode 100644 index 0000000000..e1cc872174 --- /dev/null +++ b/src/graphics/PreviewMetrics.C @@ -0,0 +1,99 @@ +/* + * \file PreviewMetrics.C + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author Angus Leeming + */ + +#include + +#ifdef __GNUG__ +#pragma implementation +#endif + +#include "PreviewMetrics.h" + +#include "debug.h" +#include "lyxlex.h" +#include "support/lyxlib.h" + + +namespace grfx { + +namespace { + +keyword_item MetricsTags[] = { + { "%%Page", 1 } +}; + +} // namespace anon + + +PreviewMetrics::PreviewMetrics(string const & file) +{ + LyXLex lex(MetricsTags, 1); + lex.setFile(file); + + if (!lex.isOK()) { + lyxerr[Debug::GRAPHICS] + << "PreviewMetricsFile(" << file << ")\n" + << "Unable to open file." << std::endl; + return; + } + + int line = 0; + while (lex.isOK()) { + + int le = lex.lex(); + switch (le) { + case LyXLex::LEX_UNDEF: + lex.printError("Unknown tag `$$Token'"); + continue; + case LyXLex::LEX_FEOF: + continue; + default: break; + } + + if (le != 1 || !lex.next()) + continue; + + int store[7]; + for (int i = 0; i < 7; ++i) { + if (lex.next()) + store[i] = lex.getInteger(); + if (lex.isOK()) + continue; + + lyxerr[Debug::GRAPHICS] + << "PreviewMetricsFile(" << file << ")\n" + << "Error reading file." << std::endl; + break; + } + + if (!lex.isOK()) + break; + + // These 7 numbers are + // bb_ll_x bb_ll_y bb_ur_x bb_ur_y ht dp wd + // We are interested only in the ratio ht:dp + double const a = double(store[4]); + double const d = double(store[5]); + double const af = a / (a + d); + store_.push_back(af); + } + + lyx::unlink(file); +} + + +double PreviewMetrics::ascent_fraction(size_type counter) +{ + if (store_.empty() || counter >= store_.size()) + return 0.5; + + return store_[counter]; +} + + +} // namespace grfx diff --git a/src/graphics/PreviewMetrics.h b/src/graphics/PreviewMetrics.h new file mode 100644 index 0000000000..10373a08ba --- /dev/null +++ b/src/graphics/PreviewMetrics.h @@ -0,0 +1,43 @@ +// -*- C++ -*- +/** + * \file PreviewMetrics.h + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author Angus Leeming + */ + +#ifndef PREVIEWMETRICS_H +#define PREVIEWMETRICS_H + +#ifdef __GNUG__ +#pragma interface +#endif + +#include "LString.h" +#include +#include + +namespace grfx { + +class PreviewMetrics : boost::noncopyable { +public: + /// + typedef std::vector::size_type size_type; + + /// Reads in file and then deletes it. + PreviewMetrics(string const & file); + + /** Return the data from the metrics file if found, + * or 0.5 + */ + double ascent_fraction(size_type); + +private: + /// + std::vector store_; +}; + +} // namespace grfx + +#endif // PREVIEWMETRICS_H diff --git a/src/graphics/Previews.C b/src/graphics/Previews.C new file mode 100644 index 0000000000..99d49c1265 --- /dev/null +++ b/src/graphics/Previews.C @@ -0,0 +1,109 @@ +/* + * \file Previews.C + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author Angus Leeming + */ + +#include + +#ifdef __GNUG__ +#pragma implementation +#endif + +#include "Previews.h" +#include "PreviewLoader.h" + +#include "buffer.h" +#include "lyxrc.h" + +#include "insets/inset.h" + +#include "support/LAssert.h" + +#include + + +namespace grfx { + +bool Previews::activated() +{ + return lyxrc.preview; +} + + +Previews & Previews::get() +{ + static Previews singleton; + return singleton; +} + + +struct Previews::Impl { + /// + typedef boost::shared_ptr PreviewLoaderPtr; + /// + typedef std::map CacheType; + /// + CacheType cache; +}; + + +Previews::Previews() + : pimpl_(new Impl()) +{} + + +Previews::~Previews() +{} + + +PreviewLoader & Previews::loader(Buffer * buffer) +{ + lyx::Assert(buffer); + + Impl::CacheType::iterator it = pimpl_->cache.find(buffer); + + if (it == pimpl_->cache.end()) { + Impl::PreviewLoaderPtr ptr(new PreviewLoader(*buffer)); + pimpl_->cache[buffer] = ptr; + return *ptr.get(); + } + + return *it->second.get(); +} + + +void Previews::removeLoader(Buffer * buffer) +{ + if (!buffer) + return; + + Impl::CacheType::iterator it = pimpl_->cache.find(buffer); + + if (it != pimpl_->cache.end()) + pimpl_->cache.erase(it); +} + + +void Previews::generateBufferPreviews(Buffer * buffer) +{ + if (!buffer || !lyxrc.preview) + return; + + PreviewLoader & ploader = loader(buffer); + + Buffer::inset_iterator it = buffer->inset_const_iterator_begin(); + Buffer::inset_iterator end = buffer->inset_const_iterator_end(); + + for (; it != end; ++it) { + if ((*it)->lyxCode() == Inset::MATH_CODE) { + (*it)->generatePreview(ploader); + } + } + + ploader.startLoading(); +} + +} // namespace grfx diff --git a/src/graphics/Previews.h b/src/graphics/Previews.h new file mode 100644 index 0000000000..7495f176ff --- /dev/null +++ b/src/graphics/Previews.h @@ -0,0 +1,73 @@ +// -*- C++ -*- +/** + * \file Previews.h + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author Angus Leeming + * + * grfx::Previews is a singleton class that stores the grfx::PreviewLoader + * for each buffer requiring one. + */ + +#ifndef PREVIEWS_H +#define PREVIEWS_H + +#ifdef __GNUG__ +#pragma interface +#endif + +#include "LString.h" +#include +#include + +class Buffer; + +namespace grfx { + +class PreviewLoader; + +class Previews : boost::noncopyable { +public: + /// a wrapper for lyxrc.preview + static bool activated(); + + /// This is a singleton class. Get the instance. + static Previews & get(); + + /** Returns the PreviewLoader for this buffer. + * Used by individual insets to update their own preview. + * We assert that (buffer != 0) but do not pass a Buffer & + * so that insets do not need to #include buffer.h + */ + PreviewLoader & loader(Buffer * buffer); + + /** Called from the Buffer d-tor. + * If (buffer == 0), does nothing. + */ + void removeLoader(Buffer * buffer); + + /** For a particular buffer, initiate the generation of previews + * for each and every snippetof LaTeX that's of interest with + * a single forked process. + * If (buffer == 0), does nothing. + */ + void generateBufferPreviews(Buffer * buffer); + +private: + /** Make the c-tor, d-tor private so we can control how many objects + * are instantiated. + */ + Previews(); + /// + ~Previews(); + + /// Use the Pimpl idiom to hide the internals. + class Impl; + /// The pointer never changes although *pimpl_'s contents may. + boost::scoped_ptr const pimpl_; +}; + +} // namespace grfx + +#endif // PREVIEWS_H diff --git a/src/insets/ChangeLog b/src/insets/ChangeLog index a2e272ba89..d0d9a64b0d 100644 --- a/src/insets/ChangeLog +++ b/src/insets/ChangeLog @@ -1,3 +1,7 @@ +2002-07-05 Angus Leeming + + * inset.h (generatePreview): new virtual method. + 2002-07-04 Lars Gullik Bjønnes * insetcommandparams.[Ch] (operator=): move out of class diff --git a/src/insets/inset.h b/src/insets/inset.h index 2bb302ecf4..5cb7972439 100644 --- a/src/insets/inset.h +++ b/src/insets/inset.h @@ -32,6 +32,9 @@ class LyXCursor; struct LaTeXFeatures; +namespace grfx { + class PreviewLoader; +} /// Insets class Inset { @@ -324,6 +327,16 @@ public: minipage somewhere, it will be the width of this minipage */ virtual int latexTextWidth(BufferView *) const; + /** Adds a LaTeX snippet to the Preview Loader for transformation + * into a bitmap image. Also connects to PreviewLoader::imageReady + * so that the inset is informed when the image has been generated + * in order to initiate its loading into LyX. + * + * Most insets have no interest in this capability, so the method + * defaults to empty. + */ + virtual void generatePreview(grfx::PreviewLoader &) const {} + protected: /// mutable int top_x; diff --git a/src/mathed/ChangeLog b/src/mathed/ChangeLog index 6e18e2cce5..0eb4e0eece 100644 --- a/src/mathed/ChangeLog +++ b/src/mathed/ChangeLog @@ -1,3 +1,13 @@ +2002-07-05 Angus Leeming + + * formula.[Ch] (generatePreview): instantiate new virtual method. + Strip out the preliminary preview code and replace with new that + makes full use of the graphics/Preview* files. Hide all the shenanigans + behind a PreviewImpl firewall. + + * math_nestinset.C (notifyCursorLeaves): update the preview using the + new graphics/Preview* code. + 2002-07-04 Lars Gullik Bjønnes * formulabase.C: ws changes diff --git a/src/mathed/formula.C b/src/mathed/formula.C index 466c9fe241..638da3eac3 100644 --- a/src/mathed/formula.C +++ b/src/mathed/formula.C @@ -26,27 +26,36 @@ #include "math_charinset.h" #include "math_arrayinset.h" #include "math_deliminset.h" +#include "math_hullinset.h" +#include "math_support.h" +#include "math_mathmlstream.h" +#include "textpainter.h" + #include "lyx_main.h" #include "BufferView.h" #include "gettext.h" #include "debug.h" +#include "lyxrc.h" + #include "support/LOstream.h" #include "support/LAssert.h" #include "support/lyxlib.h" #include "support/systemcall.h" #include "support/filetools.h" + #include "frontends/Alert.h" #include "frontends/LyXView.h" #include "frontends/Painter.h" + #include "graphics/GraphicsImage.h" -#include "lyxrc.h" -#include "math_hullinset.h" -#include "math_support.h" -#include "math_mathmlstream.h" -#include "textpainter.h" +#include "graphics/PreviewLoader.h" +#include "graphics/PreviewImage.h" +#include "graphics/Previews.h" #include #include +#include +#include #include using std::ostream; @@ -58,21 +67,57 @@ using std::vector; using std::getline; +struct InsetFormula::PreviewImpl : public boost::signals::trackable { + /// + PreviewImpl(InsetFormula & p) : parent_(p), pimage_(0) {} + + /// + void generatePreview(grfx::PreviewLoader & previewer); + + /** This method is connected to the grfx::PreviewLoader::imageReady + * signal. + */ + void previewReady(grfx::PreviewImage const &); + + /// A helper method. + string const latexString() const; + + /// + bool usePreview() const; + + /// + InsetFormula & parent_; + /// + mutable grfx::PreviewImage const * pimage_; + /// + boost::signals::connection connection_; +}; + + InsetFormula::InsetFormula() - : par_(MathAtom(new MathHullInset)), loader_(0) + : par_(MathAtom(new MathHullInset)), + preview_(new PreviewImpl(*this)) +{} + + +InsetFormula::InsetFormula(InsetFormula const & other) + : par_(other.par_), + preview_(new PreviewImpl(*this)) {} InsetFormula::InsetFormula(BufferView * bv) - : par_(MathAtom(new MathHullInset)), loader_(0) + : par_(MathAtom(new MathHullInset)), + preview_(new PreviewImpl(*this)) { view_ = bv; } InsetFormula::InsetFormula(string const & data) - : par_(MathAtom(new MathHullInset)), loader_(0) + : par_(MathAtom(new MathHullInset)), + preview_(new PreviewImpl(*this)) { if (!data.size()) return; @@ -82,6 +127,10 @@ InsetFormula::InsetFormula(string const & data) +InsetFormula::~InsetFormula() +{} + + Inset * InsetFormula::clone(Buffer const &, bool) const { return new InsetFormula(*this); @@ -173,8 +222,9 @@ void InsetFormula::draw(BufferView * bv, LyXFont const & font, MathPainterInfo pi(bv->painter()); - if (canPreview()) { - pi.pain.image(x + 1, y - a + 1, w - 2, h - 2, *(loader_->image())); + if (preview_->usePreview()) { + pi.pain.image(x + 1, y - a + 1, w - 2, h - 2, + *(preview_->pimage_->image())); } else { //pi.base.style = display() ? LM_ST_DISPLAY : LM_ST_TEXT; pi.base.style = LM_ST_TEXT; @@ -389,26 +439,23 @@ bool InsetFormula::insetAllowed(Inset::Code code) const int InsetFormula::ascent(BufferView *, LyXFont const &) const { - const int a = par_->ascent(); - if (!canPreview()) - return a + 1; - return a + 1 - (par_->height() - loader_->image()->getHeight()) / 2; + return preview_->usePreview() ? + 1 + preview_->pimage_->ascent() : 1 + par_->ascent(); } int InsetFormula::descent(BufferView *, LyXFont const &) const { - const int d = par_->descent(); - if (!canPreview()) - return d + 1; - return d + 1 - (par_->height() - loader_->image()->getHeight()) / 2; + return preview_->usePreview() ? + 1 + preview_->pimage_->descent() : 1 + par_->descent(); } int InsetFormula::width(BufferView * bv, LyXFont const & font) const { metrics(bv, font); - return canPreview() ? loader_->image()->getWidth() : par_->width(); + return preview_->usePreview() ? + preview_->pimage_->width() : par_->width(); } @@ -429,79 +476,78 @@ void InsetFormula::mutate(string const & type ) // preview stuff // -bool InsetFormula::canPreview() const +void InsetFormula::generatePreview(grfx::PreviewLoader & ploader) const { - return lyxrc.preview && loader_ && !par_->asNestInset()->editing() - && loader_->status() == grfx::Ready; + // Do nothing if no preview is desired. + if (!grfx::Previews::activated()) + return; + + preview_->generatePreview(ploader); } -void InsetFormula::statusChanged() +void InsetFormula::PreviewImpl::generatePreview(grfx::PreviewLoader & ploader) { - lyxerr << "### InsetFormula::statusChanged called!, status: " - << loader_->status() << "\n"; - if (loader_->status() == grfx::Ready) - view()->updateInset(this, false); - else if (loader_->status() == grfx::WaitingToLoad) - loader_->startLoading(); + // Generate the LaTeX snippet. + string const snippet = latexString(); + + pimage_ = ploader.preview(snippet); + if (pimage_) + return; + + // If this is the first time of calling, connect to the + // grfx::PreviewLoader signal that'll inform us when the preview image + // is ready for loading. + if (!connection_.connected()) { + connection_ = ploader.imageReady.connect( + boost::bind(&PreviewImpl::previewReady, this, _1)); + } + + ploader.add(snippet); } -void InsetFormula::updatePreview() +bool InsetFormula::PreviewImpl::usePreview() const { - // nothing to be done if no preview requested - lyxerr << "### updatePreview() called\n"; - if (!lyxrc.preview) - return; + BufferView * view = parent_.view(); + + if (!grfx::Previews::activated() || + parent_.par_->asNestInset()->editing() || + !view || !view->buffer()) + return false; + + // If the cached grfx::PreviewImage is invalid, update it. + string const snippet = latexString(); + if (!pimage_ || snippet != pimage_->snippet()) { + grfx::PreviewLoader & ploader = + grfx::Previews::get().loader(view->buffer()); + pimage_ = ploader.preview(snippet); + } - // get LaTeX + if (!pimage_) + return false; + + return pimage_->image(); +} + + +string const InsetFormula::PreviewImpl::latexString() const +{ ostringstream ls; WriteStream wi(ls, false, false); - par_->write(wi); - string const data = ls.str(); - - // the preview cache, maps contents to image loaders - typedef std::map > cache_type; - static cache_type theCache; - static int theCounter = 0; - - // set our loader corresponding to our current data - cache_type::const_iterator it = theCache.find(data); - - // is this old data? - if (it != theCache.end()) { - // we have already a loader, connect to it anyway - //lyxerr << "### updatePreview(), old loader: " << loader_ << "\n"; - loader_ = it->second.get(); - loader_->statusChanged.connect - (boost::bind(&InsetFormula::statusChanged, this)); + parent_.par_->write(wi); + return ls.str().c_str(); +} + + +void InsetFormula::PreviewImpl::previewReady(grfx::PreviewImage const & pimage) +{ + // Check snippet against the Inset's current contents + if (latexString() != pimage.snippet()) return; - } - // construct new file name - static string const dir = OnlyPath(lyx::tempName()); - ostringstream os; - os << dir << theCounter++ << ".lyxpreview"; - string file = os.str(); - - // the real work starts - //lyxerr << "### updatePreview(), new file " << file << "\n"; - std::ofstream of(file.c_str()); - of << "\\batchmode" - << "\\documentclass{article}" - << "\\usepackage{amssymb}" - << "\\thispagestyle{empty}" - << "\\pdfoutput=0" - << "\\begin{document}" - << data - << "\\end{document}\n"; - of.close(); - - // now we are done, start actual loading we will get called back via - // InsetFormula::statusChanged() if this is finished - theCache[data].reset(new grfx::Loader(file)); - //lyxerr << "### updatePreview(), new loader: " << loader_ << "\n"; - loader_ = theCache.find(data)->second.get(); - loader_->startLoading(); - loader_->statusChanged.connect(boost::bind(&InsetFormula::statusChanged, this)); + pimage_ = &pimage; + BufferView * view = parent_.view(); + if (view) + view->updateInset(&parent_, false); } diff --git a/src/mathed/formula.h b/src/mathed/formula.h index f3f49374cd..3b07ddcee6 100644 --- a/src/mathed/formula.h +++ b/src/mathed/formula.h @@ -23,8 +23,8 @@ #include "formulabase.h" #include "math_atom.h" -#include "graphics/GraphicsTypes.h" -#include "graphics/GraphicsLoader.h" + +#include class MathHullInset; @@ -38,6 +38,10 @@ public: /// explicit InsetFormula(const string & data); /// + InsetFormula(InsetFormula const &); + /// + ~InsetFormula(); + /// int ascent(BufferView *, LyXFont const &) const; /// int descent(BufferView *, LyXFont const &) const; @@ -78,6 +82,8 @@ public: /// MathAtom & par() { return par_; } /// + void generatePreview(grfx::PreviewLoader &) const; + /// void mutate(string const & type); private: @@ -91,16 +97,14 @@ private: MathHullInset const * hull() const; /// void handleExtern(string const & arg); - /// - void statusChanged(); - /// - void updatePreview(); - /// - bool canPreview() const; /// contents MathAtom par_; - /// non owning pointer - mutable grfx::Loader * loader_; + + /// Use the Pimpl idiom to hide the internals of the previewer. + class PreviewImpl; + friend class PreviewImpl; + /// The pointer never changes although *preview_'s contents may. + boost::scoped_ptr const preview_; }; #endif diff --git a/src/mathed/math_nestinset.C b/src/mathed/math_nestinset.C index e724c37a94..add5e81e5a 100644 --- a/src/mathed/math_nestinset.C +++ b/src/mathed/math_nestinset.C @@ -6,8 +6,11 @@ #include "math_cursor.h" #include "math_mathmlstream.h" #include "formulabase.h" +#include "BufferView.h" #include "debug.h" #include "frontends/Painter.h" +#include "graphics/PreviewLoader.h" +#include "graphics/Previews.h" MathNestInset::MathNestInset(idx_type nargs) @@ -240,6 +243,17 @@ MathArray MathNestInset::glue() const void MathNestInset::notifyCursorLeaves() { //lyxerr << "leaving " << *this << "\n"; - if (mathcursor) - mathcursor->formula()->updatePreview(); + if (!mathcursor || !grfx::Previews::activated()) + return; + + InsetFormulaBase * inset = mathcursor->formula(); + BufferView * bufferview = inset->view(); + if (!bufferview || !bufferview->buffer()) + return; + + grfx::Previews & previews = grfx::Previews::get(); + grfx::PreviewLoader & loader = previews.loader(bufferview->buffer()); + + inset->generatePreview(loader); + loader.startLoading(); } -- 2.39.5