]> git.lyx.org Git - features.git/commitdiff
Preview code
authorAngus Leeming <leeming@lyx.org>
Fri, 5 Jul 2002 21:24:15 +0000 (21:24 +0000)
committerAngus Leeming <leeming@lyx.org>
Fri, 5 Jul 2002 21:24:15 +0000 (21:24 +0000)
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@4538 a592a061-630c-0410-9148-cb99ea01b6c8

28 files changed:
lib/ChangeLog
lib/scripts/lyxpreview2ppm.sh [new file with mode: 0644]
lib/scripts/lyxpreview2xpm.sh [deleted file]
src/BufferView_pimpl.C
src/ChangeLog
src/LColor.C
src/LColor.h
src/buffer.C
src/frontends/ChangeLog
src/frontends/lyx_gui.h
src/frontends/xforms/ChangeLog
src/frontends/xforms/lyx_gui.C
src/graphics/ChangeLog
src/graphics/Makefile.am
src/graphics/PreviewImage.C [new file with mode: 0644]
src/graphics/PreviewImage.h [new file with mode: 0644]
src/graphics/PreviewLoader.C [new file with mode: 0644]
src/graphics/PreviewLoader.h [new file with mode: 0644]
src/graphics/PreviewMetrics.C [new file with mode: 0644]
src/graphics/PreviewMetrics.h [new file with mode: 0644]
src/graphics/Previews.C [new file with mode: 0644]
src/graphics/Previews.h [new file with mode: 0644]
src/insets/ChangeLog
src/insets/inset.h
src/mathed/ChangeLog
src/mathed/formula.C
src/mathed/formula.h
src/mathed/math_nestinset.C

index 7820c86d9f9346631f68bbde644fb9f49128d5d4..3a91d72f3e3c6f947fb0bff47a04913a1f307b9c 100644 (file)
@@ -1,3 +1,8 @@
+2002-07-05  Angus Leeming  <leeming@lyx.org>
+
+       * scripts/lyxpreview2ppm.sh: added.
+       * scripts/lyxpreview2xpm: removed.
+
 2002-06-19 Herbert Voss  <voss@perce.de>
 
        * 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 (file)
index 0000000..e662243
--- /dev/null
@@ -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} <<EOF
+# Delete everything that's enclosed between %%BeginDocument and %%EndDocument
+/^\%\%BeginDocument/,/^\%\%EndDocument/d
+
+# Extract the tightpage bounding box info.
+# Given this snippet:
+# %%Page: 1 1
+# 1 0 bop
+# -32890 -32890 32890 32890 492688 0 744653
+# The sed command gives this:
+# %%Page 1: -32890 -32890 32890 32890 492688 0 744653
+
+/^\%\%Page:/{
+  s/\: \(.*\) .*$/ \1: /;N;N
+  s/\n[^\n]*\n//p
+}
+
+# Delete everything (so only the stuff that's printed, above, goes into the
+# metrics file).
+d
+EOF
+
+# 2. Run sed!
+sed -f ${SEDSCRIPT} < ${PSFILE} > ${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 (executable)
index f7a25e4..0000000
+++ /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
index 3268e5cc2610e28d838c91b54ea3dbfb001ee109..ac2f6b663ea8b38bbcfb6b743530d7ab2fab1c39 100644 (file)
@@ -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_);
 }
 
 
index 92dff2658586475c0f4d3869a555b4b03a5cdef5..cfe063f05e688eae6678f7ba8811629843ef9e51 100644 (file)
@@ -1,3 +1,14 @@
+2002-07-05  Angus Leeming  <leeming@lyx.org>
+
+       * 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  <leeming@lyx.org>
 
        * buffer.[Ch] (makeLaTeXFile): create two methods where there was one.
index e1fe8c2d1fe34aae2fcd2cd53610936d982ddc0f..44e3004dac90ebd50f04a5f7441d605a22990e4e 100644 (file)
@@ -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" },
index b41608a0a7cef9d1d452f6d77bdaa2392fc6b333..da75feef92910bc6227432a00ba11eda2e47a67e 100644 (file)
@@ -77,6 +77,8 @@ public:
                selection,
                /// Text color in LaTeX mode
                latex,
+               /// The color used for previews
+               preview,
 
                /// Text color for notes
                note,
index a1563139d1574d091a2480aaa1a1828986fbe039..d51ac5c27a0760728518963cf0c89f1f6e116cd6 100644 (file)
@@ -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);
 }
 
 
index b911b35cc6eec99f7373d8035df13bc1ce591791..816c96626cb69770164f2b39cc1c42995bd9e909 100644 (file)
@@ -1,3 +1,7 @@
+2002-07-05  Angus Leeming  <leeming@lyx.org>
+
+       * lyx_gui.h (hexname): new function.
+
 2002-07-04  Lars Gullik Bjønnes  <larsbj@birdstep.com>
 
        * screen.C (SplashScreen): we change
index 83b674d90e713160d907e344940720837b18f3fb..98f68cd878a32bd488bf8037172fa1fb8c223160 100644 (file)
@@ -9,11 +9,9 @@
 
 #ifndef LYX_GUI_H
 #define LYX_GUI_H
-
-#include <config.h>
-
-#include "LString.h"
-
+#include "LColor.h"
+#include "LString.h" 
 #include <vector>
 
 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
index d173b53e3d57dbd0c8032e8a030590a82b9132ab..e5b2f71545d6a1746416da7eb8924f60b5b718dd 100644 (file)
@@ -1,3 +1,7 @@
+2002-07-05  Angus Leeming  <leeming@lyx.org>
+
+       * lyx_gui.C (hexname): new function.
+
 2002-07-04  Lars Gullik Bjønnes  <larsbj@birdstep.com>
 
        * lyx_gui.C (init_graphics): boost::function assign, not
index 207a33bcc807358aefbb90c9a411b394bd1a543c..bf859d2c7e2d261e3563f16ea9bb5598e136f3ad 100644 (file)
@@ -37,9 +37,9 @@
 #include "graphics/GraphicsImageXPM.h"
 #endif
 
-
+#include "Lsstream.h"
+#include <iomanip>
 #include <fcntl.h>
-
 #include <boost/bind.hpp>
 
 #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();
+}
+
index 48ac83f9c14c26a594f12ff39e3f22c6e290f13b..4210985328d962e76d459bc86ea572a19ec43164 100644 (file)
@@ -1,3 +1,16 @@
+2002-07-05  Angus Leeming  <leeming@lyx.org>
+
+       * 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  <leeming@lyx.org>
 
        * GraphicsLoader.h: whitespace.
index e000750075eea02b23d73e697f4679b41b0f7149..22ff5535bec75b954bdbd6452b3ac727e2916147 100644 (file)
@@ -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 (file)
index 0000000..8f90b20
--- /dev/null
@@ -0,0 +1,156 @@
+/**
+ *  \file PreviewImage.C
+ *  Copyright 2002 the LyX Team
+ *  Read the file COPYING
+ *
+ * \author Angus Leeming <a.leeming@ic.ac.uk>
+ */
+
+#include <config.h>
+
+#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 <boost/bind.hpp>
+#include <boost/signals/trackable.hpp>
+
+
+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<Loader> 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 (file)
index 0000000..67b67ed
--- /dev/null
@@ -0,0 +1,60 @@
+// -*- C++ -*-
+/**
+ *  \file PreviewImage.h
+ *  Copyright 2002 the LyX Team
+ *  Read the file COPYING
+ *
+ * \author Angus Leeming <a.leeming@ic.ac.uk>
+ */
+
+#ifndef PREVIEWIMAGE_H
+#define PREVIEWIMAGE_H
+
+#ifdef __GNUG__
+#pragma interface
+#endif
+
+#include "LString.h"
+#include <boost/scoped_ptr.hpp>
+
+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<Impl> const pimpl_;
+};
+
+} // namespace grfx
+
+#endif // PREVIEWIMAGE_H
diff --git a/src/graphics/PreviewLoader.C b/src/graphics/PreviewLoader.C
new file mode 100644 (file)
index 0000000..6a18ebd
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ *  \file PreviewLoader.C
+ *  Copyright 2002 the LyX Team
+ *  Read the file COPYING
+ *
+ * \author Angus Leeming <a.leeming@ic.ac.uk>
+ */
+
+#include <config.h>
+
+#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 <boost/bind.hpp>
+#include <boost/signals/trackable.hpp>
+
+#include <fstream>
+#include <iomanip>
+#include <map>
+
+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<PreviewImage> PreviewImagePtr;
+       ///
+       typedef std::map<string, PreviewImagePtr> Cache;
+       ///
+       Cache cache_;
+
+       /** The map stores the LaTeX snippet and the name of the generated
+        *  bitmap image file.
+        */
+       typedef std::map<string, string> PendingMap;
+       ///
+       PendingMap pending_;
+
+       /// Store info on a currently executing, forked process.
+       struct InProgress {
+               ///
+               typedef std::map<string, string> 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<string, InProgress> 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 &>(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<string, string> 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<StrPair> vec(pending_.begin(), pending_.end());
+       std::sort(vec.begin(), vec.end(), CompSecond());
+       
+       std::vector<StrPair>::const_iterator it  = vec.begin();
+       std::vector<StrPair>::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 (file)
index 0000000..3ba6e8f
--- /dev/null
@@ -0,0 +1,86 @@
+// -*- C++ -*-
+/**
+ *  \file PreviewLoader.h
+ *  Copyright 2002 the LyX Team
+ *  Read the file COPYING
+ *
+ * \author Angus Leeming <a.leeming@ic.ac.uk>
+ *
+ *  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 <boost/utility.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/signals/signal1.hpp>
+
+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<void, PreviewImage const &> imageReady;
+
+private:
+       /// Use the Pimpl idiom to hide the internals.
+       class Impl;
+       /// The pointer never changes although *pimpl_'s contents may.
+       boost::scoped_ptr<Impl> const pimpl_;
+};
+
+} // namespace grfx
+
+#endif // PREVIEWLOADER_H
diff --git a/src/graphics/PreviewMetrics.C b/src/graphics/PreviewMetrics.C
new file mode 100644 (file)
index 0000000..e1cc872
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  \file PreviewMetrics.C
+ *  Copyright 2002 the LyX Team
+ *  Read the file COPYING
+ *
+ * \author Angus Leeming <a.leeming@ic.ac.uk>
+ */
+
+#include <config.h>
+
+#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 (file)
index 0000000..10373a0
--- /dev/null
@@ -0,0 +1,43 @@
+// -*- C++ -*-
+/**
+ *  \file PreviewMetrics.h
+ *  Copyright 2002 the LyX Team
+ *  Read the file COPYING
+ *
+ * \author Angus Leeming <a.leeming@ic.ac.uk>
+ */
+
+#ifndef PREVIEWMETRICS_H
+#define PREVIEWMETRICS_H
+
+#ifdef __GNUG__
+#pragma interface
+#endif
+
+#include "LString.h"
+#include <boost/utility.hpp>
+#include <vector>
+
+namespace grfx {
+
+class PreviewMetrics : boost::noncopyable {
+public:
+       ///
+       typedef std::vector<PreviewMetrics>::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<double> store_;
+};
+
+} // namespace grfx
+
+#endif // PREVIEWMETRICS_H
diff --git a/src/graphics/Previews.C b/src/graphics/Previews.C
new file mode 100644 (file)
index 0000000..99d49c1
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *  \file Previews.C
+ *  Copyright 2002 the LyX Team
+ *  Read the file COPYING
+ *
+ * \author Angus Leeming <a.leeming@ic.ac.uk>
+ */
+
+#include <config.h>
+
+#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 <map>
+
+
+namespace grfx {
+
+bool Previews::activated()
+{
+       return lyxrc.preview;
+}
+
+
+Previews & Previews::get()
+{
+       static Previews singleton;
+       return singleton;
+}
+
+
+struct Previews::Impl {
+       ///
+       typedef boost::shared_ptr<PreviewLoader> PreviewLoaderPtr;
+       ///
+       typedef std::map<Buffer *, PreviewLoaderPtr> 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 (file)
index 0000000..7495f17
--- /dev/null
@@ -0,0 +1,73 @@
+// -*- C++ -*-
+/**
+ *  \file Previews.h
+ *  Copyright 2002 the LyX Team
+ *  Read the file COPYING
+ *
+ * \author Angus Leeming <a.leeming@ic.ac.uk>
+ *
+ *  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 <boost/utility.hpp>
+#include <boost/scoped_ptr.hpp>
+
+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<Impl> const pimpl_;
+};
+
+} // namespace grfx
+
+#endif // PREVIEWS_H
index a2e272ba89bf2b7be7bdb21822fe17446be13d6b..d0d9a64b0da91c3d1ec885590d8140b34bb1d2c2 100644 (file)
@@ -1,3 +1,7 @@
+2002-07-05  Angus Leeming  <leeming@lyx.org>
+
+       * inset.h (generatePreview): new virtual method.
+
 2002-07-04  Lars Gullik Bjønnes  <larsbj@birdstep.com>
 
        * insetcommandparams.[Ch] (operator=): move out of class
index 2bb302ecf48d3337b7cc33d5d75c4d54b1fa6889..5cb7972439331d759a35148d405c09fd93aebe74 100644 (file)
@@ -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;
index 6e18e2cce528491711214c667522129a708f593c..0eb4e0eeceb74d33058d001bef4487991e5adc2a 100644 (file)
@@ -1,3 +1,13 @@
+2002-07-05  Angus Leeming  <leeming@lyx.org>
+
+       * 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  <larsbj@birdstep.com>
 
        * formulabase.C: ws changes
index 466c9fe241e8872e2f217719fc1b162292eb4465..638da3eac3f0d1f055e714934fb943f3e789459b 100644 (file)
 #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 <fstream>
 #include <boost/bind.hpp>
+#include <boost/signals/trackable.hpp>
+#include <boost/signals/connection.hpp>
 #include <boost/utility.hpp>
 
 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<string, boost::shared_ptr<grfx::Loader> > 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);
 }
index f3f49374cd5ea305f8038efbc87a4a2f33e07d1c..3b07ddcee65e10fe20a703f7194137d5e415738f 100644 (file)
@@ -23,8 +23,8 @@
 
 #include "formulabase.h"
 #include "math_atom.h"
-#include "graphics/GraphicsTypes.h"
-#include "graphics/GraphicsLoader.h"
+
+#include <boost/scoped_ptr.hpp>
 
 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<PreviewImpl> const preview_;
 };
 #endif
index e724c37a941e44124e77591fa6a15362c4a31c57..add5e81e5ab7533dfa92968479d6060410723e2a 100644 (file)
@@ -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();
 }