X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Finsets%2Finsetgraphics.C;h=1dc04a8530c0eea9948d5443e3762bdede8630ab;hb=4a5b7a5952ad2381fcdf4830511293e184c7c5a1;hp=586934dd14934134ad9d177873eb245ad953af3a;hpb=b744c6ed9f4dab51e47e4a9bb0331f1740aad335;p=lyx.git diff --git a/src/insets/insetgraphics.C b/src/insets/insetgraphics.C index 586934dd14..1dc04a8530 100644 --- a/src/insets/insetgraphics.C +++ b/src/insets/insetgraphics.C @@ -3,367 +3,743 @@ * * LyX, The Document Processor * - * Copyright 1995 Matthias Ettrich - * Copyright 1995-2000 the LyX Team. - * + * Copyright 1995-2002 the LyX Team. + * + * \author Baruch Even + * \author Herbert Voss * ====================================================== */ +/* +Known BUGS: + + * If the image is from the clipart, and the document is moved to another + directory, the user is screwed. Need a way to handle it. + This amounts to a problem of when to use relative or absolute file paths + 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. + [Note that browseRelFile in helper_funcs.* provides a file name + which is relative if it is at reference path (here puffer path) + level or below, and an absolute path if the file name is not a + `natural' relative file name. In any case, + MakeAbsPath(filename, buf->filePath()) + is guaranteed to provide the correct absolute path. This is what is + done know for include insets. Feel free to ask me -- JMarc + 14/01/2002] + + * 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. + +TODO Before initial production release: + * Replace insetfig everywhere + * Search for comments of the form + // INSET_GRAPHICS: remove this when InsetFig is thrown. + 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. + * 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 + to find it in the clipart, or in the same directory with the image. + * 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. + */ + +/* NOTES: + * Fileformat: + * Current version is 1 (inset file format version), when changing it + * it should be changed in the Write() function when writing in one place + * and when reading one should change the version check and the error message. + * The filename is kept in the lyx file in a relative way, so as to allow + * moving the document file and its images with no problem. + * + * + * Conversions: + * Postscript output means EPS figures. + * + * PDF output is best done with PDF figures if it's a direct conversion + * or PNG figures otherwise. + * Image format + * from to + * EPS epstopdf + * PS ps2pdf + * JPG/PNG direct + * PDF direct + * others PNG + */ + +#include + #ifdef __GNUG__ #pragma implementation -#endif - -#include +#endif #include "insets/insetgraphics.h" +#include "insets/insetgraphicsParams.h" + +#include "graphics/GraphicsCache.h" +#include "graphics/GraphicsCacheItem.h" + +#include "LyXView.h" +#include "buffer.h" #include "BufferView.h" +#include "converter.h" #include "Painter.h" -#include "form_graphics.h" #include "lyx_gui_misc.h" -#include "filedlg.h" +#include "lyxtext.h" +#include "lyxrc.h" +#include "font.h" +#include "debug.h" +#include "gettext.h" + +#include "frontends/Dialogs.h" +#include "frontends/Alert.h" +#include "frontends/controllers/helper_funcs.h" +#include "frontends/support/LyXImage.h" + #include "support/FileInfo.h" #include "support/filetools.h" +#include "support/lyxlib.h" +#include "support/lyxmanip.h" +#include "support/lyxalgo.h" + +#include +#include +extern string system_tempdir; + +using std::ifstream; using std::ostream; using std::endl; +using std::max; +using std::vector; -extern string system_lyxdir; -extern string user_lyxdir; -extern string system_tempdir; -string browseFile(); +/////////////////////////////////////////////////////////////////////////// +int const VersionNumber = 1; +/////////////////////////////////////////////////////////////////////////// -extern "C" void GraphicxCB(FL_OBJECT * obj, long arg) +// This function is a utility function +// ... that should be with ChangeExtension ... +inline +string const RemoveExtension(string const & filename) { - lyxerr << "GraphicxCB: obj = " << obj << " arg = " << arg << endl; - switch (arg) { - case 0: // The graphics file - lyxerr << "Set the graphics file in InsetGraphics" << endl; - break; - case 1: // The file browser - browseFile(); - break; - case 2: // The Apply button - lyxerr << "Scan the form and set the " - "InsetGraphics accordingly." << endl; - break; - case 3: // The OK button - GraphicxCB(obj, 2); // do the apply - GraphicxCB(obj, 4); // do the cancel - break; - case 4: // The Cancel button - lyxerr << "Just hide the form and do nothing else!" << endl; - break; - case 99: - lyxerr << "Not implemented yet..." << endl; - break; - default: - lyxerr << "Unknown callback value!" << endl; - break; - } + return ChangeExtension(filename, string()); } -string browseFile() +namespace { + +string const unique_id() { - // This function is probably not good enough yet, and does need some - // arguemnts to tell what dir to start looking in. - - static string current_figure_path = "."; + static unsigned int seed = 1000; - LyXFileDlg fileDlg; + ostringstream ost; + ost << "graph" << ++seed; - // Does user clipart directory exist? - string bufclip = AddName (user_lyxdir, "clipart"); - FileInfo fileInfo(bufclip); - if (!(fileInfo.isOK() && fileInfo.isDir())) - // No - bail out to system clipart directory - bufclip = AddName (system_lyxdir, "clipart"); + // Needed if we use lyxstring. + return ost.str().c_str(); +} +} // namespace anon - fileDlg.SetButton(0, _("Clipart"), bufclip); - bool error = false; - string buf; - do { - string p = fileDlg.Select(_("Graphics"), - current_figure_path, - "*ps", string()); +// Initialize only those variables that do not have a constructor. +InsetGraphics::InsetGraphics() + : cacheHandle(0), imageLoaded(false), graphic_label(unique_id()) +{} - if (p.empty()) return p; - current_figure_path = OnlyPath(p); +InsetGraphics::InsetGraphics(InsetGraphics const & ig, bool same_id) + : Inset(), SigC::Object() + , cacheHandle(ig.cacheHandle) + , imageLoaded(ig.imageLoaded) + , graphic_label(unique_id()) +{ + setParams(ig.getParams()); + if (same_id) + id_ = ig.id_; +} - if (p.find_first_of("#~$% ") != string::npos) { - WriteAlert(_("Filename can't contain any " - "of these characters:"), - // xgettext:no-c-format - _("space, '#', '~', '$' or '%'.")); - error = true; - } - } while (error); - return buf; +InsetGraphics::~InsetGraphics() +{ + // Emits the hide signal to the dialog connected (if any) + hideDialog(); } -InsetGraphics::InsetGraphics() - : form(0) -{} +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::Converting: + msg = _("Converting Image"); + break; + case GraphicsCacheItem::ErrorConverting: + msg = _("Error converting"); + break; + case GraphicsCacheItem::Loaded: + // No message to write. + break; + } + } + return msg; +} -int InsetGraphics::ascent(Painter &, LyXFont const &) const +int InsetGraphics::ascent(BufferView *, LyXFont const &) const { - - return 100; + LyXImage * pixmap = 0; + if (cacheHandle.get() && (pixmap = cacheHandle->getImage())) + return pixmap->getHeight(); + else + return 50; } -int InsetGraphics::descent(Painter &, LyXFont const &) const +int InsetGraphics::descent(BufferView *, LyXFont const &) const { // this is not true if viewport is used and clip is not. - return 1; + return 0; } -int InsetGraphics::width(Painter &, LyXFont const &) const +int InsetGraphics::width(BufferView *, LyXFont const & font) const { - if (bb.isSet()) { - return bb.urx - bb.llx; + 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 = max(font_width, msg_width); + } + + return max(50, font_width + 15); } - return 100; } void InsetGraphics::draw(BufferView * bv, LyXFont const & font, - int baseline, float & x, bool) const + int baseline, float & x, bool) const { - Painter & pain = bv->painter(); - - // This will draw the graphics. As for now we only draw a - // placeholder rectangele. - pain.rectangle(int(x), baseline - ascent(pain, font), - width(pain, font), - ascent(pain, font) + descent(pain, font)); + Painter & paint = bv->painter(); + + 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 (imageLoaded) { + + paint.image(old_x + 2, baseline - lascent, + lwidth - 4, lascent + ldescent, + cacheHandle->getImage()); + } else { + // Get the image status, default to unknown error. + GraphicsCacheItem::ImageStatus status = GraphicsCacheItem::UnknownError; + if (lyxrc.use_gui && 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); + // Print the file name. + LyXFont msgFont(font); + msgFont.setFamily(LyXFont::SANS_FAMILY); + 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); + } + } } -void InsetGraphics::Edit(BufferView *, int, int, unsigned int) +void InsetGraphics::edit(BufferView *bv, int, int, unsigned int) { - lyxerr.debug() << "InsetGraphics::Edit" << endl; - - if (!form) { - form = create_form_Graphics(); - fl_set_form_atclose(form->Graphics, CancelCloseBoxCB, 0); - fl_set_object_return(form->Angle, FL_RETURN_ALWAYS); - fl_set_object_return(form->Width, FL_RETURN_ALWAYS); - fl_set_object_return(form->Height, FL_RETURN_ALWAYS); - } + bv->owner()->getDialogs()->showGraphics(this); +} - if (form->Graphics->visible) { - fl_raise_form(form->Graphics); - } else { - fl_show_form(form->Graphics, FL_PLACE_MOUSE | FL_PLACE_SIZE, - FL_FULLBORDER, _("Graphics")); - } + +void InsetGraphics::edit(BufferView * bv, bool) +{ + edit(bv, 0, 0, 0); } -Inset::EDITABLE InsetGraphics::Editable() const +Inset::EDITABLE InsetGraphics::editable() const { return IS_EDITABLE; } -void InsetGraphics::Write(Buffer const *, ostream & os) const +void InsetGraphics::write(Buffer const * buf, ostream & os) const { - // The question on the file format is still open. - // Suggestions? - // perhaps a format that is xml-parsable - // - os << "GRAPHICS\n"; + os << "Graphics FormatVersion " << VersionNumber << '\n'; + params.Write(buf, os); } -void InsetGraphics::Read(Buffer const *, LyXLex & /*lex*/) +void InsetGraphics::read(Buffer const * buf, LyXLex & lex) { - // For now we only use a static file... - graphicsfile = "testfile.xpm"; - //graphicscache.addFile(graphicsfile); - //bb = graphicscache.getBB(graphicsfile); - //pixmap = graphicscache.getPixmap(graphicsfile); -} + 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"; -int InsetGraphics::Latex(Buffer const *, ostream & os, - bool /*fragile*/, bool/*fs*/) const + updateInset(); +} + +void InsetGraphics::readInsetGraphics(Buffer const * buf, LyXLex & lex) { - // MISSING: We have to decide how to do the order of the options - // that is depentant of order, like witdth, height, andlge. Should - // we rotate before scale? Should we let the user decide? - // bool rot_before_scale; ? - // Nothing to do if we don't have a graphics file - if (graphicsfile.empty()) return 0; - - // We never used the starred form, we use the "clip" option instead. - string command("\\insetgraphics"); - -#ifdef HAVE_SSTREAM - std::ostringstream options; -#else - ostrstream options; -#endif - 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,"; - break; - case RIGHTBOTTOM: - options << "origin=rb,"; - break; + 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 == "FormatVersion") { + lex.next(); + int version = lex.getInteger(); + if (version > VersionNumber) + lyxerr + << "This document was created with a newer Graphics widget" + ", You should use a newer version of LyX to read this" + " file." + << endl; + // TODO: Possibly open up a dialog? + } + else { + if (! params.Read(buf, lex, token)) + lyxerr << "Unknown token, " << token << ", skipping." + << endl; } } - 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 << ","; +} + +// FormatVersion < 1.0 (LyX < 1.2) +void InsetGraphics::readFigInset(Buffer const * buf, LyXLex & lex) +{ + vector const oldUnits = + getVectorFromString("pt,cm,in,p%,c%"); + bool finished = false; + // set the display default + if (lyxrc.display_graphics == "mono") + params.display = InsetGraphicsParams::MONOCHROME; + else if (lyxrc.display_graphics == "gray") + params.display = InsetGraphicsParams::GRAYSCALE; + else if (lyxrc.display_graphics == "color") + params.display = InsetGraphicsParams::COLOR; + else + params.display = InsetGraphicsParams::NONE; + 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 = buf->filePath(); + 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(); + params.subcaption = true; + } else if (token == "label") { + if (lex.next()); + // kept for backwards compability. Delete in 0.13.x + } else if (token == "angle") { + if (lex.next()) + params.rotate = true; + params.rotateAngle = lex.getFloat(); + } else if (token == "size") { + if (lex.next()) + params.lyxwidth = LyXLength(lex.getString()+"pt"); + if (lex.next()) + params.lyxheight = LyXLength(lex.getString()+"pt"); + } else if (token == "flags") { + if (lex.next()) + switch (lex.getInteger()) { + case 1: params.display = InsetGraphicsParams::MONOCHROME; + break; + case 2: params.display = InsetGraphicsParams::GRAYSCALE; + break; + case 3: params.display = InsetGraphicsParams::COLOR; + break; + } + } else if (token == "subfigure") { + params.subcaption = true; + } else if (token == "width") { + if (lex.next()) { + int i = lex.getInteger(); + if (lex.next()) { + if (i == 5) { + params.scale = lex.getInteger(); + params.size_type = InsetGraphicsParams::SCALE; + } else { + params.width = LyXLength(lex.getString()+oldUnits[i]); + params.size_type = InsetGraphicsParams::WH; + } + } + } + } else if (token == "height") { + if (lex.next()) { + int i = lex.getInteger(); + if (lex.next()) { + params.height = LyXLength(lex.getString()+oldUnits[i]); + params.size_type = InsetGraphicsParams::WH; + } + } + } } - if (clip) { - options << "clip,"; +} + +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. + ostringstream options; + if (!params.bb.empty()) + options << " bb=" << strip(params.bb) << ",\n"; + if (params.draft) + options << " draft,\n"; + if (params.clip) + options << " clip,\n"; + if (params.size_type == InsetGraphicsParams::WH) { + if (!params.width.zero()) + options << " width=" << params.width.asLatexString() << ",\n"; + if (!params.height.zero()) + options << " height=" << params.height.asLatexString() << ",\n"; + } else if (params.size_type == InsetGraphicsParams::SCALE) { + if (params.scale > 0) + options << " scale=" << double(params.scale)/100.0 << ",\n"; } - if (draft) { - options << "draft,"; + if (params.keepAspectRatio) + options << " keepaspectratio,\n"; + // 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) && params.rotate) { + options << " angle=" << params.rotateAngle << ",\n"; + if (!params.rotateOrigin.empty()) { + options << " origin=" << params.rotateOrigin[0]; + if (contains(params.rotateOrigin,"Top")) + options << 't'; + else if (contains(params.rotateOrigin,"Bottom")) + options << 'b'; + else if (contains(params.rotateOrigin,"Baseline")) + options << 'B'; + options << ",\n"; + } } - if (!type.empty()) { - options << "type=" << type << ","; + if (!params.special.empty()) + options << params.special << ",\n"; + string opts = options.str().c_str(); + return opts.substr(0,opts.size()-2); // delete last ",\n" +} + +namespace { +string decideOutputImageFormat(string const & suffix) +{ + // lyxrc.pdf_mode means: + // Are we creating a PDF or a PS file? + // (Should actually mean, are we using latex or pdflatex). + lyxerr[Debug::INFO] << "decideOutput::lyxrc.pdf_mode = " << lyxrc.pdf_mode << "\n"; + if (lyxrc.pdf_mode) { + if (contains(suffix,"ps") || suffix == "pdf") + return "pdf"; + else if (suffix == "jpg") + return suffix; + else + return "png"; } - if (!ext.empty()) { - options << "ext=" << type << ","; + // If it's postscript, we always do eps. + lyxerr[Debug::INFO] << "decideOutput: we have PostScript mode\n"; + if (suffix != "ps") + return "eps"; + else + return "ps"; +} + +} // 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 + // + // if it's a zipped one, than let LaTeX do the rest!!! + if ((zippedFile(params.filename) && params.noUnzip) || buf->niceFile) { + lyxerr[Debug::INFO] << "don't unzip file or export latex" + << params.filename << endl; + return params.filename; } - if (!read.empty()) { - options << "read=" << type << ","; + string filename_ = params.filename; + if (zippedFile(filename_)) + filename_ = unzipFile(filename_); + // now we have unzipped files + // Get the extension (format) of the original file. + // we handle it like a virtual one, so we can have + // different extensions with the same type. + string const extension = getExtFromContents(filename_); + // are we usind latex ((e)ps) or pdflatex (pdf,jpg,png) + string const image_target = decideOutputImageFormat(extension); + if (extension == image_target) // :-) + return filename_; +// commented out to check if the "not exist"bug is fixed. +// if (!IsFileReadable(filename_)) { // :-( +// Alert::alert(_("File") + params.filename, +// _("isn't readable or doesn't exists!")); +// return filename_; +// } + string outfile; + string const temp = AddName(buf->tmppath, filename_); + outfile = RemoveExtension(temp); + lyxerr[Debug::INFO] << "tempname = " << temp << "\n"; + lyxerr[Debug::INFO] << "buf::tmppath = " << buf->tmppath << "\n"; + lyxerr[Debug::INFO] << "filename_ = " << filename_ << "\n"; + lyxerr[Debug::INFO] << "outfile = " << outfile << endl; + converters.convert(buf, filename_, outfile, extension, image_target); + return outfile; +} + + +int InsetGraphics::latex(Buffer const *buf, ostream & os, + bool /*fragile*/, bool/*fs*/) const +{ + // 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"; + return 1; // One end of line marker added to the stream. } - if (!command.empty()) { - options << "command=" << type << ","; + // These 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) { + before += "\\subfigure[" + params.subcaptionText + "]{"; + after = '}'; } -#ifdef HAVE_SSTREAM - string opts(options.str().c_str()); -#else - options << '\0'; - char * tmp = options.str(); - string opts(tmp); - delete [] tmp; -#endif - opts = strip(opts, ','); + // We never use the starred form, we use the "clip" option instead. + before += "\\includegraphics"; + // Write the options if there are any. + string const opts = createLatexOptions(); if (!opts.empty()) { - command += "["; - command += opts; - command += "]"; + before += ("[%\n" + opts +']'); } - command += "{"; - command += graphicsfile; - command += "}"; - - os << command << '\n'; - - return 1; + // Make the filename relative to the lyx file + // and remove the extension so the LaTeX will use whatever is + // appropriate (when there are several versions in different formats) + string const latex_str = before + '{' + prepareFile(buf) + '}' + after; + os << latex_str; + // Return how many newlines we issued. + int const newlines = + int(lyx::count(latex_str.begin(), latex_str.end(),'\n') + 1); + // lyxerr << "includegraphics: " << newlines << " lines of text" + // << endl; + return newlines; } -int InsetGraphics::Ascii(Buffer const *, ostream &) const +int InsetGraphics::ascii(Buffer const *, ostream & os, int) const { + // 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. + // at least we send the filename + os << '<' << _("Graphicfile:") << params.filename << ">\n"; 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 *, ostream & os) const { + // 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 << ""; return 0; } -void InsetGraphics::Validate(LaTeXFeatures & /*features*/) const +void InsetGraphics::validate(LaTeXFeatures & features) const { - //features.graphicx = true; + // If we have no image, we should not require anything. + if (params.filename.empty()) + return ; + + features.includeFile(graphic_label, RemoveExtension(params.filename)); + + features.require("graphicx"); + + if (params.subcaption) + features.require("subfigure"); } -Inset * InsetGraphics::Clone() const +// Update the inset after parameters changed (read from file or changed in +// dialog. +void InsetGraphics::updateInset() const { - return new InsetGraphics; + GraphicsCache & gc = GraphicsCache::getInstance(); + boost::shared_ptr temp(0); + + // We do it this way so that in the face of some error, we will still + // be in a valid state. + InsetGraphicsParams::DisplayType local_display = params.display; + if (local_display == InsetGraphicsParams::DEFAULT) { + if (lyxrc.display_graphics == "mono") + local_display = InsetGraphicsParams::MONOCHROME; + else if (lyxrc.display_graphics == "gray") + local_display = InsetGraphicsParams::GRAYSCALE; + else if (lyxrc.display_graphics == "color") + local_display = InsetGraphicsParams::COLOR; + else + local_display = InsetGraphicsParams::NONE; + } + + if (!params.filename.empty() && lyxrc.use_gui && + local_display != InsetGraphicsParams::NONE) { + temp = gc.addFile(params.filename); + } + + // Mark the image as unloaded so that it gets updated. + imageLoaded = false; + + cacheHandle = temp; +} + + +bool InsetGraphics::setParams(InsetGraphicsParams const & p) +{ + // If nothing is changed, just return and say so. + if (params == p) + return false; + + // Copy the new parameters. + params = p; + + // Update the inset with the new parameters. + updateInset(); + + // We have changed data, report it. + return true; } +InsetGraphicsParams InsetGraphics::getParams() const +{ + return params; +} + + +Inset * InsetGraphics::clone(Buffer const &, bool same_id) const +{ + return new InsetGraphics(*this, same_id); +} +