]> git.lyx.org Git - lyx.git/blobdiff - src/insets/insetgraphics.C
2001-12-28 Lars Gullik Bj�nnes <larsbj@birdstep.com>
[lyx.git] / src / insets / insetgraphics.C
index af5035b804c226a0bd7b05a5d9c6e13c45c11570..b4f7f7cd5ea4255c4f5d571a557725c3b0ba559f 100644 (file)
@@ -3,20 +3,19 @@
  * 
  *           LyX, The Document Processor
  *      
- *           Copyright 1995-2000 the LyX Team.
+ *           Copyright 1995-2001 the LyX Team.
  *           
  *           This file Copyright 2000 Baruch Even.
  * ====================================================== */
 
 /*
-How to use it for now:
-    * The lyxfunc 'graphics-insert' will insert this inset into the document.
-*/
+Major tasks:
+       * Switch to convert the images in the background, this requires work on
+               the converter, the systemcontroller and the graphics cache.
 
-/*
-Immediate tasks:
-    * Add the GraphicsCache and FormatTranslator in order to get inline
-        viewing of the figures.
+Minor tasks:
+    * Pop up a dialog if the widget version is higher than what we accept.
+       * Provide sed/awk/C code to downgrade from InsetGraphics to FigInset(?)
         
 */
 
@@ -29,63 +28,56 @@ Known BUGS:
        We should probably use what the user asks to use... but when he chooses
        by the file dialog we normally get an absolute path and this may not be 
        what the user meant.
-    * Bug in FileDlg class (src/filedlg.[hC]) when selecting a file and then
-        pressing ok, it counts as if no real selection done. Apparently it
-        when choosing a file it doesn't update the select file input line.
-Current PROBLEMS:
-    
-    * How to support both PDF and PS output, should we do the conversion
-        or should we just give the bounding box and tell latex how to do the
-        conversion itself?
-        I (Baruch Even) tend towards doing the conversion ourselves, otherwise
-        we need to give latex quite a few translation commands and from the
-        graphicx package docs it appears that it takes quite a bit of memory
-        on the side of TeXing.
-       
-       * How do we handle the inline viewing? we may need to show the same image
-               in several formats (color, monochrome, grayscale) or even in different
-               sizes, not to mention rotations!
+
+       * If we are trying to create a file in a read-only directory and there
+               are graphics that need converting, the converting will fail because
+               it is done in-place, into the same directory as the original image.
+               This needs to be fixed in the src/converter.C file
+               [ This is presumed to be fixed, needs testing.]
+
+       * We do not dither or resize the image in a WYSIWYM way, we load it at
+               its original size and color, resizing is done in the final output,
+               but not in the LyX window.
+
+       * The scale option is only handled for the horizontal part, the vertical
+               part will not work. For now it is also shown only for horizontal
+               resizing on the form.
+
+       * EPS figures are not fully detected, they may have a lot of possible
+               suffixes so we need to read the file and detect if it's EPS or not.
+               [Implemented, need testing]
                
-TODO Basics:
-    * Add support for more features so that it will be better than insetfig.
-        * Keep aspect ratio radio button
-    * Create the GraphicsCache and FormatTranslator
-    * Add inline viewing of image.
 TODO Before initial production release:
     * Replace insetfig everywhere
-        * Read it's file format
-        * Get created by all commands used to create figinset currently.
         * Search for comments of the form
             // INSET_GRAPHICS: remove this when InsetFig is thrown.
-          And act upon them.
+          And act upon them. Make sure not to remove InsetFig code for the 
+                 1.2.0 release, only afterwards, after deployment shows InsetGraphics
+                 to be ok.
  
-    * Pop up a dialog if the widget version is higher than what we accept.
-    * Finish the basic To-do list.
-    * Extract the general logic of the dialog in order to allow easier porting
-        to Gnome/KDE, and put the general logic in frontends and the inherited
-        platform dependent code in the appropriate dirs.
-   
 TODO Extended features:
  
     * Advanced Latex tab folder.
-    * Add even more options to make it better than insetfig.
+    * Add support for more features so that it will be better than insetfig.
+        * Keep aspect ratio radio button
         * Support for complete control over the latex parameters for TeXperts
         * What advanced features the users want to do?
             Implement them in a non latex dependent way, but a logical way.
             LyX should translate it to latex or any other fitting format.
     * Add a way to roll the image file into the file format.
-    * When loading if the image is not found in the expected place, try
+    * When loading, if the image is not found in the expected place, try
        to find it in the clipart, or in the same directory with the image.
-    * If the dialog had no real change from previous time, do not mark document
-        as changed.
     * Keep a tab on the image file, if it changes, update the lyx view.
        * The image choosing dialog could show thumbnails of the image formats
                it knows of, thus selection based on the image instead of based on
                filename.
+       * Add support for the 'picins' package.
+       * Add support for the 'picinpar' package.
+       * Improve support for 'subfigure' - Allow to set the various options
+               that are possible.
+       * Add resizing by percentage of image size (50%, 150%) - usefull for two
+               images of different size to be resized where they both should have
+               the same scale compared to each other.
  */
 
 /* NOTES:
@@ -122,37 +114,18 @@ TODO Extended features:
  * moving the document file and its images with no problem.
  *
  * Conversions:
- *  
- *  Apparently the PNG output is preferred over PDF images when doing PDF
- *  documents (i.e. prefer imagemagick eps2png over eps2pdf)
- */
-
-/* Current Stage:
- *  Embryonic.
- *
- * PLAN:
- *  Finish basic support:
- *      Inline image viewing
- *      Get into lyx-devel as an unactivated inset for the benefit of those
- *          who really need it.
+ *   Postscript output means EPS figures.
  *
- *  Do Release quality support:
- *      Allow to change display depth
- *      Make default figure instead of InsetFig
- *      Add to LyX (probably after 1.1.6 is released)
- *      
- *  Extended features:
- *      Output format conversion
- *      Print depth changes
- *      Image file tracking of changes.
- *
- *  Extended^2:
- *      Image roll-in (how? when? why?)
- *          This means to add the image inside the LyX file, usefull when
- *          transferring the file around.
+ *   PDF output is best done with PDF figures if it's a direct conversion
+ *   or PNG figures otherwise.
+ *     Image format
+ *     from        to
+ *     EPS         epstopdf
+ *     JPG/PNG     direct
+ *     PDF         direct
+ *     others      PNG
  */
 
-
 #include <config.h> 
 
 #ifdef __GNUG__
@@ -168,40 +141,100 @@ TODO Extended features:
 #include "LyXView.h"
 #include "buffer.h"
 #include "BufferView.h"
+#include "converter.h"
+#include "frontends/support/LyXImage.h"
 #include "Painter.h"
 #include "lyx_gui_misc.h"
-#include "filedlg.h"
 #include "support/FileInfo.h"
 #include "support/filetools.h"
+#include "support/lyxlib.h"
 #include "lyxtext.h"
-
+#include "lyxrc.h"
+#include "font.h" // For the lyxfont class.
+#include "fstream" // for ifstream in isEPS
+#include <algorithm> // For the std::max
+#include "support/lyxmanip.h"
 #include "debug.h"
+#include "gettext.h"
 
+extern string system_tempdir;
 
+using std::ifstream;
 using std::ostream;
 using std::endl;
 
 
+// This function is a utility function
+// ... that should be with ChangeExtension ...
+inline
+string const RemoveExtension(string const & filename)
+{
+       return ChangeExtension(filename, string());
+}
+
+
 // Initialize only those variables that do not have a constructor.
 InsetGraphics::InsetGraphics()
-#ifdef IG_OLDPARAMS
-       : use_bb(false), hiresbb(false), angle(0.0), origin(DEFAULT)
-       , keepaspectratio(false), scale(0.0), clip(false), draft(false)
-       , cacheHandle(0)
-#endif 
-       : cacheHandle(0), pixmapInitialized(false)
+       : cacheHandle(0), imageLoaded(false)
 {}
 
+
+InsetGraphics::InsetGraphics(InsetGraphics const & ig, bool same_id)
+       : Inset(), SigC::Object()
+       , cacheHandle(ig.cacheHandle)
+       , imageLoaded(ig.imageLoaded)
+{
+       setParams(ig.getParams());
+       if (same_id)
+               id_ = ig.id_;
+}
+
+
 InsetGraphics::~InsetGraphics()
 {
        // Emits the hide signal to the dialog connected (if any)
-       hide();
+       hideDialog();
 }
 
+
+string const
+InsetGraphics::statusMessage() const
+{
+       string msg;
+
+       if (cacheHandle.get()) {
+               switch (cacheHandle->getImageStatus()) {
+               case GraphicsCacheItem::UnknownError:
+                       msg = _("Unknown Error");
+                       break;
+
+               case GraphicsCacheItem::Loading:
+                       msg = _("Loading...");
+                       break;
+
+               case GraphicsCacheItem::ErrorReading:
+                       msg = _("Error reading");
+                       break;
+
+               case GraphicsCacheItem::ErrorConverting:
+                       msg = _("Error converting");
+                       break;
+
+               case GraphicsCacheItem::Loaded:
+                       // No message to write.
+                       break;
+               }
+       }
+
+       return msg;
+}
+
+
 int InsetGraphics::ascent(BufferView *, LyXFont const &) const
 {
-       if (pixmapInitialized)
-               return cacheHandle->getHeight();
+       LyXImage * pixmap = 0;
+       if (cacheHandle.get() && (pixmap = cacheHandle->getImage()))
+               return pixmap->getHeight();
        else
                return 50;
 }
@@ -214,12 +247,33 @@ int InsetGraphics::descent(BufferView *, LyXFont const &) const
 }
 
 
-int InsetGraphics::width(BufferView *, LyXFont const &) const
+int InsetGraphics::width(BufferView *, LyXFont const & font) const
 {
-       if (pixmapInitialized)
-               return cacheHandle->getWidth();
-       else
-               return 50;
+       LyXImage * pixmap = 0;
+       
+       if (cacheHandle.get() && (pixmap = cacheHandle->getImage()))
+               return pixmap->getWidth();
+       else {
+               int font_width = 0;
+
+               LyXFont msgFont(font);
+               msgFont.setFamily(LyXFont::SANS_FAMILY);
+
+               string const justname = OnlyFilename (params.filename);
+               if (!justname.empty()) {
+                       msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
+                       font_width = lyxfont::width(justname, msgFont);
+               }
+
+               string const msg = statusMessage();
+               if (!msg.empty()) {
+                       msgFont.setSize(LyXFont::SIZE_TINY);
+                       int const msg_width = lyxfont::width(msg, msgFont);
+                       font_width = std::max(font_width, msg_width);
+               }
+               
+               return std::max(50, font_width + 15);
+       }
 }
 
 
@@ -228,121 +282,115 @@ void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
 {
        Painter & paint = bv->painter();
 
-       int lwidth = width(bv, font);
        int ldescent = descent(bv, font);
        int lascent = ascent(bv, font);
+       int lwidth = width(bv, font);
+
+       // Make sure x is updated upon exit from this routine
+       int old_x = int(x);
+       x += lwidth;
 
        // This will draw the graphics. If the graphics has not been loaded yet,
        // we draw just a rectangle.
-       if (pixmapInitialized) {
+       if (imageLoaded) {
 
-               paint.pixmap(int(x) + 2, baseline - lascent,
-                            lwidth - 4, lascent + ldescent,
-                            pixmap);
+               paint.image(old_x + 2, baseline - lascent,
+                           lwidth - 4, lascent + ldescent,
+                           cacheHandle->getImage());
        } else {
-               paint.rectangle(int(x) + 2, baseline - lascent,
+               
+               // Get the image status, default to unknown error.
+               GraphicsCacheItem::ImageStatus status = GraphicsCacheItem::UnknownError;
+               if (lyxrc.display_graphics != "no" &&
+                   params.display != InsetGraphicsParams::NONE &&
+                   cacheHandle.get())
+                       status = cacheHandle->getImageStatus();
+               
+               // Check if the image is now ready.
+               if (status == GraphicsCacheItem::Loaded) {
+                       imageLoaded = true;
+
+                       // Tell BufferView we need to be updated!
+                       bv->text->status(bv, LyXText::CHANGED_IN_DRAW);
+                       return;
+               }
+
+               paint.rectangle(old_x + 2, baseline - lascent,
                                lwidth - 4,
                                lascent + ldescent);
 
-               // Check if the image is now ready.
-               if (cacheHandle &&
-                       (cacheHandle->getImageStatus() == GraphicsCacheItem::Loaded)) {
-                       pixmap = cacheHandle->getImage();
-                       pixmapInitialized = true;
+               // Print the file name.
+               LyXFont msgFont(font);
+               msgFont.setFamily(LyXFont::SANS_FAMILY);
 
-                       // Tell BufferView we need to be updated!
-                       bv->text->status = LyXText::CHANGED_IN_DRAW;
+               string const justname = OnlyFilename (params.filename);
+               if (!justname.empty()) {
+                       msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
+                       paint.text(old_x + 8, 
+                                  baseline - lyxfont::maxAscent(msgFont) - 4,
+                                  justname, msgFont);
+               }
+
+               // Print the message.
+               string const msg = statusMessage();
+               if (!msg.empty()) {
+                       msgFont.setSize(LyXFont::SIZE_TINY);
+                       paint.text(old_x + 8, baseline - 4, msg, msgFont);
                }
        }
+}
 
-       // Add the image width to the row width.
-       x += lwidth;
+
+void InsetGraphics::edit(BufferView *bv, int, int, unsigned int)
+{
+       bv->owner()->getDialogs()->showGraphics(this);
 }
 
 
-void InsetGraphics::Edit(BufferView *bv, int, int, unsigned int)
+void InsetGraphics::edit(BufferView * bv, bool)
 {
-       bv->owner()->getDialogs() -> showGraphics(this);
+       edit(bv, 0, 0, 0);
 }
 
 
-Inset::EDITABLE InsetGraphics::Editable() const
+Inset::EDITABLE InsetGraphics::editable() const
 {
        return IS_EDITABLE;
 }
 
 
-void InsetGraphics::Write(Buffer const * buf, ostream & os) const
+void InsetGraphics::write(Buffer const * buf, ostream & os) const
 {
-       os << "GRAPHICS FormatVersion 1" << endl;
+       os << "Graphics FormatVersion 1\n";
 
        params.Write(buf, os);
 }
 
-#if 0
-// Baruch Even 2000-07-08
-
-// A Thought for another way to read the file...
-// The map should be a static part of the object or a static part of this
-// file and should be filled during program start.
-// The questions are:
-// 1. Is this cleaner?
-// 2. Is there no hidden performance costs?
-//
-// Regarding 2 I can already see that we will have two copies of the strings
-// one in the data part of the program and one in the map, but that won't be
-// more than say 2K (overestimation here), there is no real benefit to put
-// it in the map since there aren't that many configuration items that will
-// make it a faster solution, it might just be a bit cleaner.
-// (a map stores either in a hash or a kind of a balanced tree).
-
-void InsetGraphics::Read(Buffer const * buf, LyXLex & lex)
-{
-       typedef map < string, enum TOKENS > ReadActionMap;
-       static ReadActionMap const readMap;
-
-       bool finished = false;
-
-       while (lex.IsOK() && !finished) {
-               lex.next();
-
-               string const token = lex.GetString();
-               lyxerr.debug() << "Token: '" << token << '\'' << endl;
-
-               if (token.empty())
-                       continue;
-
-               ReadActionMap::const_iterator it =
-                   readMap.find(token);
 
-               if (it == readMap.end()) {
-                       lyxerr << "Unknown keyword, skipping." << endl;
-                       continue;
-               }
-
-               switch (it.second) {
-               case FILENAME_TOKEN:
-                       break;
-               case VERSION_TOKEN:
-                       break;
-               default:
-                       break;
-               }
+void InsetGraphics::read(Buffer const * buf, LyXLex & lex)
+{
+       string const token = lex.getString();
 
+       if (token == "Graphics")
+               readInsetGraphics(buf, lex);
+       else if (token == "Figure") // Compatibility reading of FigInset figures.
+               readFigInset(buf, lex);
+       else
+               lyxerr[Debug::INFO] << "Not a Graphics or Figure inset!\n";
 
-       }
+       updateInset();
 }
-#endif 
 
-void InsetGraphics::Read(Buffer const * buf, LyXLex & lex)
+void InsetGraphics::readInsetGraphics(Buffer const * buf, LyXLex & lex)
 {
        bool finished = false;
 
-       while (lex.IsOK() && !finished) {
+       while (lex.isOK() && !finished) {
                lex.next();
 
-               string const token = lex.GetString();
-               lyxerr.debug() << "Token: '" << token << '\'' << endl;
+               string const token = lex.getString();
+               lyxerr[Debug::INFO] << "Token: '" << token << '\'' 
+                                   << std::endl;
 
                if (token.empty()) {
                        continue;
@@ -350,25 +398,96 @@ void InsetGraphics::Read(Buffer const * buf, LyXLex & lex)
                        finished = true;
                } else if (token == "FormatVersion") {
                        lex.next();
-                       int version = lex.GetInteger();
+                       int version = lex.getInteger();
                        if (version > 1)
                                lyxerr
                                << "This document was created with a newer Graphics widget"
                                ", You should use a newer version of LyX to read this"
                                " file."
-                               << endl;
+                               << std::endl;
                        // TODO: Possibly open up a dialog?
                }
                else {
                        if (! params.Read(buf, lex, token))
-                               lyxerr << "Unknown token, " << token << ",skipping." << endl;
+                               lyxerr << "Unknown token, " << token << ", skipping." 
+                                       << std::endl;
                }
        }
+}
 
-       updateInset();
+
+void InsetGraphics::readFigInset(Buffer const * buf, LyXLex & lex)
+{
+       bool finished = false;
+       
+       while (lex.isOK() && !finished) {
+               lex.next();
+
+               string const token = lex.getString();
+               lyxerr[Debug::INFO] << "Token: " << token << endl;
+               
+               if (token.empty())
+                       continue;
+               else if (token == "\\end_inset") {
+                       finished = true;
+               } else if (token == "file") {
+                       if (lex.next()) {
+                               string const name = lex.getString();
+                               string const path = OnlyPath(buf->fileName());
+                               params.filename = MakeAbsPath(name, path);
+                       }
+               } else if (token == "extra") {
+                       if (lex.next());
+                       // kept for backwards compability. Delete in 0.13.x
+               } else if (token == "subcaption") {
+                       if (lex.eatLine())
+                               params.subcaptionText = lex.getString();
+               } else if (token == "label") {
+                       if (lex.next());
+                       // kept for backwards compability. Delete in 0.13.x
+               } else if (token == "angle") {
+                       if (lex.next())
+                               params.rotateAngle = lex.getFloat();
+               } else if (token == "size") {
+                       // Size of image on screen is ignored in InsetGraphics, just eat
+                       // the input.
+                       if (lex.next()) {
+                               lex.getInteger();
+                       }
+                       if (lex.next()) {
+                               lex.getInteger();
+                       }
+               } else if (token == "flags") {
+                       InsetGraphicsParams::DisplayType tmp = InsetGraphicsParams::COLOR;
+                       if (lex.next())
+                               switch (lex.getInteger()) {
+                               case 1: tmp = InsetGraphicsParams::MONOCHROME; break;
+                               case 2: tmp = InsetGraphicsParams::GRAYSCALE; break;
+                               }
+                       params.display = tmp;
+               } else if (token == "subfigure") {
+                       params.subcaption = true;
+               } else if (token == "width") {
+                       if (lex.next()) {
+                               params.widthResize = static_cast<InsetGraphicsParams::Resize>(lex.getInteger());
+                       }
+                       if (lex.next()) {
+                               params.widthSize = lex.getFloat();
+                       }
+               } else if (token == "height") {
+                       if (lex.next()) {
+                               params.heightResize = static_cast<InsetGraphicsParams::Resize>(lex.getInteger());
+                       }
+                       if (lex.next()) {
+                               params.heightSize = lex.getFloat();
+                       }
+               }
+       }
 }
 
-static
+
+namespace {
+
 void formatResize(ostream & os, string const & key,
                  InsetGraphicsParams::Resize resizeType, double size)
 {
@@ -392,264 +511,314 @@ void formatResize(ostream & os, string const & key,
                os << key << '=' << size / 100 << "\\column" << key << ',';
                break;
 
+       case InsetGraphicsParams::SCALE:
+               os << "scale" << '=' << size/100 << ',';
        }
 }
 
-int InsetGraphics::Latex(Buffer const *buf, ostream & os,
-                         bool /*fragile*/, bool/*fs*/) const
-{
-       // MISSING: We have to decide how to do the order of the options
-       // that is dependent of order, like witdth, height, angle. Should
-       // we rotate before scale? Should we let the user decide?
-       // bool rot_before_scale; ?
+} // namespace anon
 
-       // (BE) As a first step we should do a scale before rotate since this is
-       // more like the natural thought of how to do it.
-       // (BE) I believe that a priority list presented to the user with
-       // a default order would be the best, though it would be better to
-       // hide such a thing in an "Advanced options" dialog.
-       // (BE) This should go an advanced LaTeX options dialog.
-
-       // If there is no file specified, just output a message about it in
-       // the latex output.
-       if (params.filename.empty()) {
-               os << "\\fbox{\\rule[-0.5in]{0pt}{1in}"
-               << _("empty figure path")
-               << '}'
-               << endl;
-
-               return 1;
-       }
 
+string const
+InsetGraphics::createLatexOptions() const
+{
        // Calculate the options part of the command, we must do it to a string
        // stream since we might have a trailing comma that we would like to remove
        // before writing it to the output stream.
-       std::ostringstream options;
+       ostringstream options;
 
        formatResize(options, "width", params.widthResize, params.widthSize);
        formatResize(options, "height", params.heightResize, params.heightSize);
 
-       if (params.rotateAngle != 0) {
+       // Make sure it's not very close to zero, a float can be effectively
+       // zero but not exactly zero.
+       if (!lyx::float_equal(params.rotateAngle, 0, 0.001)) {
                options << "angle="
-               << params.rotateAngle << ',';
+                       << params.rotateAngle << ',';
        }
 
-#ifdef IG_OLDPARAMS
-       if (bb.isSet() && use_bb) {
-               options << "bb="
-               << bb.llx << ' ' << bb.lly << ' '
-               << bb.urx << ' ' << bb.ury << ',';
-       }
-       if (hiresbb) {
-               options << "hiresbb,";
-       }
-       if (viewport.isSet()) {
-               options << "viewport="
-               << viewport.llx << ' ' << viewport.lly << ' '
-               << viewport.urx << ' ' << viewport.ury << ',';
-       }
-       if (trim.isSet()) {
-               options << "trim="
-               << trim.llx << ' ' << trim.lly << ' '
-               << trim.urx << ' ' << trim.ury << ',';
-       }
-       if (natheight.value() != 0) {
-               options << "natheight=" << natheight.asString() << ',';
-       }
-       if (natwidth.value() != 0) {
-               options << "natwidth=" << natwidth.asString() << ',';
-       }
-       if (angle != 0.0) {
-               options << "angle=" << angle << ',';
-       }
-       if (origin != DEFAULT) {
-               switch (origin) {
-               case DEFAULT: break;
-               case LEFTTOP:
-                       options << "origin=lt,";
-                       break;
-               case LEFTCENTER:
-                       options << "origin=lc,";
-                       break;
-               case LEFTBASELINE:
-                       options << "origin=lB,";
-                       break;
-               case LEFTBOTTOM:
-                       options << "origin=lb,";
-                       break;
-               case CENTERTOP:
-                       options << "origin=ct,";
-                       break;
-               case CENTER:
-                       options << "origin=c,";
-                       break;
-               case CENTERBASELINE:
-                       options << "origin=cB,";
-                       break;
-               case CENTERBOTTOM:
-                       options << "origin=cb,";
-                       break;
-               case RIGHTTOP:
-                       options << "origin=rt,";
-                       break;
-               case RIGHTCENTER:
-                       options << "origin=rc,";
-                       break;
-               case RIGHTBASELINE:
-                       options << "origin=rB,";
+       string opts = options.str().c_str();
+       opts = strip(opts, ',');
+
+       return opts;
+}
+
+namespace {
+
+enum FileType {
+       EPS,
+       PNG,
+       JPEG,
+       GIF,
+       PDF,
+       UNKNOWN
+};
+
+bool isEPS(string const & filename)
+{
+       if (filename.empty() || !IsFileReadable(filename)) return false;
+
+       ifstream ifs(filename.c_str());
+
+       if (!ifs) return false; // Couldn't open file...
+
+       bool is_eps = false; // Have we recognized the file as EPS?
+       string to_find = "%!PS-Adobe-"; // The string we use to recognize
+       int const max_attempts = 500; // Maximum strings to read to attempt recognition
+       int count = 0; // Counter of attempts.
+       string str;
+       for (; count < max_attempts; ++count) {
+               if (ifs.eof()) {
+                       lyxerr[Debug::INFO] << "InsetGraphics (isEPS)"
+                               " End of file reached and it wasn't found to be EPS!" << endl;
                        break;
-               case RIGHTBOTTOM:
-                       options << "origin=rb,";
+               }
+
+               ifs >> str;
+               if (str.find(to_find)) {
+                       is_eps = true;
                        break;
                }
        }
-       if (g_width.value() != 0) {
-               options << "width=" << g_width.asString() << ',';
-       }
-       if (g_height.value() != 0) {
-               options << "height=" << g_height.asString() << ',';
-       }
-       if (totalheight.value() != 0) {
-               options << "totalheight=" << totalheight.asString() << ',';
-       }
-       if (keepaspectratio) {
-               options << "keepaspectratio,";
-       }
-       if (scale != 0.0) {
-               options << "scale=" << scale << ',';
-       }
-       if (clip) {
-               options << "clip,";
-       }
-       if (draft) {
-               options << "draft,";
+
+       return is_eps;
+}
+
+enum FileType classifyFileType(string const & filename, string const & suffix)
+{
+       if (suffix == "png")
+               return PNG;
+       else if (suffix == "jpg" || suffix == "jpeg")
+               return JPEG;
+       else if (suffix == "gif")
+               return GIF;
+       else if (suffix == "pdf")
+               return PDF;
+       else if (isEPS(filename))
+               return EPS;
+
+       return UNKNOWN;
+}
+
+string decideOutputImageFormat(string const & suffix, enum FileType type)
+{
+       // lyxrc.pdf_mode means:
+       // Are we creating a PDF or a PS file?
+       // (Should actually mean, are we using latex or pdflatex).
+       
+       if (lyxrc.pdf_mode) {
+               if (type == EPS || type == EPS || type == PDF)
+                       return "pdf";
+               else if (type == JPEG)
+                       return suffix;
+               else
+                       return "png";
        }
-       if (!type.empty()) {
-               options << "type=" << type << ',';
 
-               // These should be present only when type is used.
-               if (!ext.empty()) {
-                       options << "ext=" << type << ',';
-               }
-               if (!read.empty()) {
-                       options << "read=" << type << ',';
-               }
-               if (!command.empty()) {
-                       options << "command=" << type << ',';
-               }
+       // If it's postscript, we always do eps.
+       // There are many suffixes that are actually EPS (ask Garst for example)
+       // so we detect if it's an EPS by looking in the file, if it is, we return
+       // the same suffix of the file so it won't be converted.
+       if (type == EPS)
+               return suffix;
+       
+       return "eps";
+}
+
+} // Anon. namespace
+
+string const 
+InsetGraphics::prepareFile(Buffer const *buf) const
+{
+
+       // do_convert = Do we need to convert the file?
+       // nice = Do we create a nice version?
+       //        This is used when exporting the latex file only.
+       // 
+       // 
+       // if (!do_convert)
+       //   return original filename
+       // 
+       // if (!nice)
+       //   convert_place = temp directory
+       //   return new filename in temp directory
+       // else
+       //   convert_place = original file directory
+       //   return original filename without the extension
+       //
+       
+       // Get the extension (format) of the original file.
+       string const extension = GetExtension(params.filename);
+       FileType type = classifyFileType(params.filename, extension);
+       
+       // Are we creating a PDF or a PS file?
+       // (Should actually mean, are we usind latex or pdflatex).
+       string const image_target = decideOutputImageFormat(extension, type);
+
+       if (extension == image_target)
+               return params.filename;
+
+       string outfile;
+       if (!buf->niceFile) {
+               string const temp = AddName(buf->tmppath, params.filename);
+               outfile = RemoveExtension(temp);
+               
+               //lyxerr << "buf::tmppath = " << buf->tmppath << "\n";
+               //lyxerr << "filename = " << params.filename << "\n";
+               //lyxerr << "temp = " << temp << "\n";
+               //lyxerr << "outfile = " << outfile << endl;
+       } else {
+               string const path = OnlyPath(buf->fileName());
+               string const relname = MakeRelPath(params.filename, path);
+               outfile = RemoveExtension(relname);
        }
-#endif 
 
-       string opts(options.str().c_str());
-       opts = strip(opts, ',');
+       converters.convert(buf, params.filename, outfile, extension, image_target);
+       
+       return outfile;
+}
+
+
+int InsetGraphics::latex(Buffer const *buf, ostream & os,
+                        bool /*fragile*/, bool/*fs*/) const
+{
+       // MISSING: We have to decide how to do the order of the options
+       // that is dependent of order, like width, height, angle. Should
+       // we rotate before scale? Should we let the user decide?
+       // bool rot_before_scale; ?
+
+       // (BE) As a first step we should do a scale before rotate since this is
+       // more like the natural thought of how to do it.
+       // (BE) I believe that a priority list presented to the user with
+       // a default order would be the best, though it would be better to
+       // hide such a thing in an "Advanced options" dialog.
+       // (BE) This should go an advanced LaTeX options dialog.
 
+       // If there is no file specified, just output a message about it in
+       // the latex output.
+       if (params.filename.empty()) {
+               os  << "\\fbox{\\rule[-0.5in]{0pt}{1in}"
+                       << _("empty figure path")
+                       << "}\n";
 
-       // If it's not an inline image, surround it with the centering paragraph.
-       if (! params.inlineFigure) {
-               os << endl
-               << "\\vspace{0.3cm}" << endl
-               << "{\\par\\centering ";
+               return 1; // One end of line marker added to the stream.
        }
 
+       // Keep count of newlines that we issued.
+       int newlines = 0;
+
+       // This variables collect all the latex code that should be before and
+       // after the actual includegraphics command.
+       string before;
+       string after;
+
        // Do we want subcaptions?
        if (params.subcaption) {
-               os << "\\subfigure[" << params.subcaptionText << "]{";
+               before += "\\subfigure[" + params.subcaptionText + "]{";
+               after = '}' + after;
        }
 
-       // We never used the starred form, we use the "clip" option instead.
-       os << "\\includegraphics";
+       // We never use the starred form, we use the "clip" option instead.
+       os << before << "\\includegraphics";
 
+       // Write the options if there are any.
+       string const opts = createLatexOptions();
        if (!opts.empty()) {
                os << '[' << opts << ']';
        }
 
        // Make the filename relative to the lyx file
-       string filename = MakeRelPath(params.filename, OnlyPath(buf->fileName()));
-
        // and remove the extension so the LaTeX will use whatever is
        // appropriate (when there are several versions in different formats)
-       filename = ChangeExtension(filename, string());
-
-       os << '{' << filename << '}';
-
-       // Do we want a subcaption?
-       if (params.subcaption) {
-               // Close the subcaption command
-               os << '}';
-       }
-
-       // Is this an inline graphics?
-       if (!params.inlineFigure) {
-               os << " \\par}" << endl
-               << "\\vspace{0.3cm}" << endl;
-       }
-
-       // How do we decide to what format should we export?
-       //    cacheHandle->>export(ImageType::EPS);
-       //    cacheHandle->>export(ImageType::PNG);
+       string const filename = prepareFile(buf);
+       
+       os << '{' << filename << '}' << after;
 
-       return 1;
+       // Return how many newlines we issued.
+       return newlines;
 }
 
 
-int InsetGraphics::Ascii(Buffer const *, ostream &, int) const
+int InsetGraphics::ascii(Buffer const *, ostream &, int) const
 {
-       // No graphics in ascii output.
+       // No graphics in ascii output. Possible to use gifscii to convert
+       // images to ascii approximation.
+       
+       // 1. Convert file to ascii using gifscii
+       // 2. Read ascii output file and add it to the output stream.
+       
        return 0;
 }
 
 
-int InsetGraphics::Linuxdoc(Buffer const *, ostream &) const
+int InsetGraphics::linuxdoc(Buffer const *, ostream &) const
 {
        // No graphics in LinuxDoc output. Should check how/what to add.
        return 0;
 }
 
 
-int InsetGraphics::DocBook(Buffer const *, ostream &) const
+// For explanation on inserting graphics into DocBook checkout:
+// http://linuxdoc.org/LDP/LDP-Author-Guide/inserting-pictures.html
+// See also the docbook guide at http://www.docbook.org/
+int InsetGraphics::docbook(Buffer const * buf, ostream & os) const
 {
-       // No graphics in DocBook output. Should check how/what to add.
+       // Change the path to be relative to the main file.
+       string const buffer_dir = OnlyPath(buf->fileName());
+       string const filename = RemoveExtension(
+                                  MakeRelPath(params.filename, buffer_dir));
+
+       // In DocBook v5.0, the graphic tag will be eliminated from DocBook, will 
+       // need to switch to MediaObject. However, for now this is sufficient and 
+       // easier to use.
+       os << "<graphic fileref=\"" << filename << "\"></graphic>";
        return 0;
 }
 
 
-void InsetGraphics::Validate(LaTeXFeatures & features) const
+void InsetGraphics::validate(LaTeXFeatures & features) const
 {
        // If we have no image, we should not require anything.
        if (params.filename.empty())
                return ;
 
-       features.graphicx = true;
+       features.require("graphicx");
 
        if (params.subcaption)
-               features.subfigure = true;
+               features.require("subfigure");
 }
 
+
 // Update the inset after parameters changed (read from file or changed in
 // dialog.
-void InsetGraphics::updateInset()
+void InsetGraphics::updateInset() const
 {
-       // If file changed...
-
-       GraphicsCache * gc = GraphicsCache::getInstance();
-       GraphicsCacheItem * temp = 0;
-
-       if (!params.filename.empty()) {
-               temp = gc->addFile(params.filename);
+       GraphicsCache & gc = GraphicsCache::getInstance();
+       boost::shared_ptr<GraphicsCacheItem> temp(0);
+
+       // We do it this way so that in the face of some error, we will still
+       // be in a valid state.
+       if (!params.filename.empty() &&
+           lyxrc.display_graphics != "no" &&
+           params.display != InsetGraphicsParams::NONE) {
+               temp = gc.addFile(params.filename);
        }
 
-       delete cacheHandle;
+       // Mark the image as unloaded so that it gets updated.
+       imageLoaded = false;
+
        cacheHandle = temp;
 }
 
-bool InsetGraphics::setParams(InsetGraphicsParams const & params)
+
+bool InsetGraphics::setParams(InsetGraphicsParams const & p)
 {
        // If nothing is changed, just return and say so.
-       if (this->params == params)
+       if (params == p)
                return false;
 
        // Copy the new parameters.
-       this->params = params;
+       params = p;
 
        // Update the inset with the new parameters.
        updateInset();
@@ -658,20 +827,14 @@ bool InsetGraphics::setParams(InsetGraphicsParams const & params)
        return true;
 }
 
+
 InsetGraphicsParams InsetGraphics::getParams() const
 {
        return params;
 }
 
-Inset * InsetGraphics::Clone(Buffer const &) const
-{
-       InsetGraphics * newInset = new InsetGraphics;
 
-       newInset->cacheHandle = cacheHandle;
-       newInset->pixmap = pixmap;
-       newInset->pixmapInitialized = pixmapInitialized;
-
-       newInset->setParams(getParams());
-
-       return newInset;
+Inset * InsetGraphics::clone(Buffer const &, bool same_id) const
+{
+       return new InsetGraphics(*this, same_id);
 }