]> git.lyx.org Git - lyx.git/blob - src/insets/insetgraphics.C
Bug fix from Herbert
[lyx.git] / src / insets / insetgraphics.C
1 /* This file is part of
2  * ====================================================== 
3  * 
4  *           LyX, The Document Processor
5  *       
6  *           Copyright 1995-2002 the LyX Team.
7  *           
8  * \author Baruch Even
9  * \author Herbert Voss <voss@lyx.org>
10  * ====================================================== */
11
12 /*
13 Known BUGS:
14     
15     * If the image is from the clipart, and the document is moved to another
16        directory, the user is screwed. Need a way to handle it.
17        This amounts to a problem of when to use relative or absolute file paths
18        We should probably use what the user asks to use... but when he chooses
19        by the file dialog we normally get an absolute path and this may not be 
20        what the user meant.
21        [Note that browseRelFile in helper_funcs.* provides a file name
22         which is relative if it is at reference path (here puffer path)
23         level or below, and an absolute path if the file name is not a
24         `natural' relative file name. In any case,
25             MakeAbsPath(filename, buf->filePath())
26         is guaranteed to provide the correct absolute path. This is what is
27         done know for include insets. Feel free to ask me -- JMarc
28         14/01/2002]
29         
30         * If we are trying to create a file in a read-only directory and there
31                 are graphics that need converting, the converting will fail because
32                 it is done in-place, into the same directory as the original image.
33                 This needs to be fixed in the src/converter.C file
34                 [ This is presumed to be fixed, needs testing.]
35
36         * We do not dither or resize the image in a WYSIWYM way, we load it at
37                 its original size and color, resizing is done in the final output,
38                 but not in the LyX window.
39
40 TODO Before initial production release:
41     * Replace insetfig everywhere
42         * Search for comments of the form
43             // INSET_GRAPHICS: remove this when InsetFig is thrown.
44           And act upon them. Make sure not to remove InsetFig code for the 
45                   1.2.0 release, only afterwards, after deployment shows InsetGraphics
46                   to be ok.
47         * What advanced features the users want to do?
48             Implement them in a non latex dependent way, but a logical way.
49             LyX should translate it to latex or any other fitting format.
50     * Add a way to roll the image file into the file format.
51     * When loading, if the image is not found in the expected place, try
52        to find it in the clipart, or in the same directory with the image.
53     * Keep a tab on the image file, if it changes, update the lyx view.
54         * The image choosing dialog could show thumbnails of the image formats
55           it knows of, thus selection based on the image instead of based on
56           filename.
57         * Add support for the 'picins' package.
58         * Add support for the 'picinpar' package.
59         * Improve support for 'subfigure' - Allow to set the various options
60                 that are possible.
61  */
62
63 /* NOTES:
64  * Fileformat:
65  * Current version is 1 (inset file format version), when changing it
66  * it should be changed in the Write() function when writing in one place
67  * and when reading one should change the version check and the error message.
68  * The filename is kept in  the lyx file in a relative way, so as to allow
69  * moving the document file and its images with no problem.
70  * 
71  *
72  * Conversions:
73  *   Postscript output means EPS figures.
74  *
75  *   PDF output is best done with PDF figures if it's a direct conversion
76  *   or PNG figures otherwise.
77  *      Image format
78  *      from        to
79  *      EPS         epstopdf
80  *      PS          ps2pdf
81  *      JPG/PNG     direct
82  *      PDF         direct
83  *      others      PNG
84  */
85
86 #include <config.h> 
87
88 #ifdef __GNUG__
89 #pragma implementation
90 #endif 
91
92 #include "insets/insetgraphics.h"
93 #include "insets/insetgraphicsParams.h"
94 #include "graphics/GraphicsCache.h"
95 #include "graphics/GraphicsCacheItem.h"
96
97 #include "frontends/Dialogs.h"
98 #include "frontends/Alert.h"
99 #include "LyXView.h"
100 #include "buffer.h"
101 #include "BufferView.h"
102 #include "converter.h"
103 #include "frontends/support/LyXImage.h"
104 #include "Painter.h"
105 #include "lyx_gui_misc.h"
106 #include "support/FileInfo.h"
107 #include "support/filetools.h"
108 #include "support/lyxalgo.h" // lyx::count
109 #include "support/lyxlib.h"
110 #include "frontends/controllers/helper_funcs.h"
111 #include "lyxtext.h"
112 #include "lyxrc.h"
113 #include "font.h" // For the lyxfont class.
114 #include "fstream" // for ifstream in isEPS
115 #include <algorithm> // For the std::max
116 #include "support/lyxmanip.h"
117 #include "debug.h"
118 #include "gettext.h"
119 //#include "LOStream.h"
120 #include "support/lyxalgo.h"
121
122 extern string system_tempdir;
123
124 using std::ifstream;
125 using std::ostream;
126 using std::endl;
127
128 ///////////////////////////////////////////////////////////////////////////
129 int const VersionNumber = 1;
130 ///////////////////////////////////////////////////////////////////////////
131
132 // This function is a utility function
133 // ... that should be with ChangeExtension ...
134 inline
135 string const RemoveExtension(string const & filename)
136 {
137         return ChangeExtension(filename, string());
138 }
139
140
141 // Initialize only those variables that do not have a constructor.
142 InsetGraphics::InsetGraphics()
143         : cacheHandle(0), imageLoaded(false)
144 {}
145
146
147 InsetGraphics::InsetGraphics(InsetGraphics const & ig, bool same_id)
148         : Inset(), SigC::Object()
149         , cacheHandle(ig.cacheHandle)
150         , imageLoaded(ig.imageLoaded)
151 {
152         setParams(ig.getParams());
153         if (same_id)
154                 id_ = ig.id_;
155 }
156
157
158 InsetGraphics::~InsetGraphics()
159 {
160         // Emits the hide signal to the dialog connected (if any)
161         hideDialog();
162 }
163
164
165 string const InsetGraphics::statusMessage() const
166 {
167         string msg;
168         if (cacheHandle.get()) {
169                 switch (cacheHandle->getImageStatus()) {
170                 case GraphicsCacheItem::UnknownError:
171                         msg = _("Unknown Error");
172                         break;
173                 case GraphicsCacheItem::Loading:
174                         msg = _("Loading...");
175                         break;
176                 case GraphicsCacheItem::ErrorReading:
177                         msg = _("Error reading");
178                         break;
179                 case GraphicsCacheItem::Converting:
180                         msg = _("Converting Image");
181                         break;
182                 case GraphicsCacheItem::ErrorConverting:
183                         msg = _("Error converting");
184                         break;
185                 case GraphicsCacheItem::Loaded:
186                         // No message to write.
187                         break;
188                 }
189         }
190         return msg;
191 }
192
193
194 int InsetGraphics::ascent(BufferView *, LyXFont const &) const
195 {
196         LyXImage * pixmap = 0;
197         if (cacheHandle.get() && (pixmap = cacheHandle->getImage()))
198                 return pixmap->getHeight();
199         else
200                 return 50;
201 }
202
203
204 int InsetGraphics::descent(BufferView *, LyXFont const &) const
205 {
206         // this is not true if viewport is used and clip is not.
207         return 0;
208 }
209
210
211 int InsetGraphics::width(BufferView *, LyXFont const & font) const
212 {
213         LyXImage * pixmap = 0;
214         
215         if (cacheHandle.get() && (pixmap = cacheHandle->getImage()))
216                 return pixmap->getWidth();
217         else {
218                 int font_width = 0;
219
220                 LyXFont msgFont(font);
221                 msgFont.setFamily(LyXFont::SANS_FAMILY);
222
223                 string const justname = OnlyFilename (params.filename);
224                 if (!justname.empty()) {
225                         msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
226                         font_width = lyxfont::width(justname, msgFont);
227                 }
228
229                 string const msg = statusMessage();
230                 if (!msg.empty()) {
231                         msgFont.setSize(LyXFont::SIZE_TINY);
232                         int const msg_width = lyxfont::width(msg, msgFont);
233                         font_width = std::max(font_width, msg_width);
234                 }
235                 
236                 return std::max(50, font_width + 15);
237         }
238 }
239
240
241 void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
242                          int baseline, float & x, bool) const
243 {
244         Painter & paint = bv->painter();
245
246         int ldescent = descent(bv, font);
247         int lascent = ascent(bv, font);
248         int lwidth = width(bv, font);
249
250         // Make sure x is updated upon exit from this routine
251         int old_x = int(x);
252         x += lwidth;
253
254         // This will draw the graphics. If the graphics has not been loaded yet,
255         // we draw just a rectangle.
256         if (imageLoaded) {
257
258                 paint.image(old_x + 2, baseline - lascent,
259                             lwidth - 4, lascent + ldescent,
260                             cacheHandle->getImage());
261         } else {        
262                 // Get the image status, default to unknown error.
263                 GraphicsCacheItem::ImageStatus status = GraphicsCacheItem::UnknownError;
264                 if (lyxrc.use_gui && params.display != InsetGraphicsParams::NONE &&
265                     cacheHandle.get())
266                         status = cacheHandle->getImageStatus();
267                 // Check if the image is now ready.
268                 if (status == GraphicsCacheItem::Loaded) {
269                         imageLoaded = true;
270                         // Tell BufferView we need to be updated!
271                         bv->text->status(bv, LyXText::CHANGED_IN_DRAW);
272                         return;
273                 }
274                 paint.rectangle(old_x + 2, baseline - lascent,
275                                 lwidth - 4,
276                                 lascent + ldescent);
277                 // Print the file name.
278                 LyXFont msgFont(font);
279                 msgFont.setFamily(LyXFont::SANS_FAMILY);
280                 string const justname = OnlyFilename (params.filename);
281                 if (!justname.empty()) {
282                         msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
283                         paint.text(old_x + 8, 
284                                    baseline - lyxfont::maxAscent(msgFont) - 4,
285                                    justname, msgFont);
286                 }
287                 // Print the message.
288                 string const msg = statusMessage();
289                 if (!msg.empty()) {
290                         msgFont.setSize(LyXFont::SIZE_TINY);
291                         paint.text(old_x + 8, baseline - 4, msg, msgFont);
292                 }
293         }
294 }
295
296
297 void InsetGraphics::edit(BufferView *bv, int, int, unsigned int)
298 {
299         bv->owner()->getDialogs()->showGraphics(this);
300 }
301
302
303 void InsetGraphics::edit(BufferView * bv, bool)
304 {
305         edit(bv, 0, 0, 0);
306 }
307
308
309 Inset::EDITABLE InsetGraphics::editable() const
310 {
311         return IS_EDITABLE;
312 }
313
314
315 void InsetGraphics::write(Buffer const * buf, ostream & os) const
316 {
317         os << "Graphics FormatVersion " << VersionNumber << '\n';
318         params.Write(buf, os);
319 }
320
321
322 void InsetGraphics::read(Buffer const * buf, LyXLex & lex)
323 {
324         string const token = lex.getString();
325
326         if (token == "Graphics")
327                 readInsetGraphics(buf, lex);
328         else if (token == "Figure") // Compatibility reading of FigInset figures.
329                 readFigInset(buf, lex);
330         else
331                 lyxerr[Debug::INFO] << "Not a Graphics or Figure inset!\n";
332
333         updateInset();
334 }
335
336 void InsetGraphics::readInsetGraphics(Buffer const * buf, LyXLex & lex)
337 {
338         bool finished = false;
339
340         while (lex.isOK() && !finished) {
341                 lex.next();
342
343                 string const token = lex.getString();
344                 lyxerr[Debug::INFO] << "Token: '" << token << '\'' 
345                                     << std::endl;
346
347                 if (token.empty()) {
348                         continue;
349                 } else if (token == "\\end_inset") {
350                         finished = true;
351                 } else if (token == "FormatVersion") {
352                         lex.next();
353                         int version = lex.getInteger();
354                         if (version > VersionNumber)
355                                 lyxerr
356                                 << "This document was created with a newer Graphics widget"
357                                 ", You should use a newer version of LyX to read this"
358                                 " file."
359                                 << std::endl;
360                         // TODO: Possibly open up a dialog?
361                 }
362                 else {
363                         if (! params.Read(buf, lex, token))
364                                 lyxerr << "Unknown token, " << token << ", skipping." 
365                                         << std::endl;
366                 }
367         }
368 }
369
370 // FormatVersion < 1.0  (LyX < 1.2)
371 void InsetGraphics::readFigInset(Buffer const * buf, LyXLex & lex)
372 {
373         std::vector<string> const oldUnits =
374                 getVectorFromString("pt,cm,in,p%,c%");
375         bool finished = false;
376         // set the display default      
377         if (lyxrc.display_graphics == "mono") 
378             params.display = InsetGraphicsParams::MONOCHROME;
379         else if (lyxrc.display_graphics == "gray") 
380             params.display = InsetGraphicsParams::GRAYSCALE;
381         else if (lyxrc.display_graphics == "color") 
382             params.display = InsetGraphicsParams::COLOR;
383         else
384             params.display = InsetGraphicsParams::NONE;
385         while (lex.isOK() && !finished) {
386                 lex.next();
387
388                 string const token = lex.getString();
389                 lyxerr[Debug::INFO] << "Token: " << token << endl;
390                 
391                 if (token.empty())
392                         continue;
393                 else if (token == "\\end_inset") {
394                         finished = true;
395                 } else if (token == "file") {
396                         if (lex.next()) {
397                                 string const name = lex.getString();
398                                 string const path = buf->filePath();
399                                 params.filename = MakeAbsPath(name, path);
400                         }
401                 } else if (token == "extra") {
402                         if (lex.next());
403                         // kept for backwards compability. Delete in 0.13.x
404                 } else if (token == "subcaption") {
405                         if (lex.eatLine())
406                                 params.subcaptionText = lex.getString();
407                         params.subcaption = true;
408                 } else if (token == "label") {
409                         if (lex.next());
410                         // kept for backwards compability. Delete in 0.13.x
411                 } else if (token == "angle") {
412                         if (lex.next())
413                                 params.rotate = true;
414                                 params.rotateAngle = lex.getFloat();
415                 } else if (token == "size") {
416                         if (lex.next())
417                                 params.lyxwidth = LyXLength(lex.getString()+"pt");
418                         if (lex.next())
419                                 params.lyxheight = LyXLength(lex.getString()+"pt");
420                 } else if (token == "flags") {
421                         if (lex.next())
422                                 switch (lex.getInteger()) {
423                                 case 1: params.display = InsetGraphicsParams::MONOCHROME; 
424                                     break;
425                                 case 2: params.display = InsetGraphicsParams::GRAYSCALE; 
426                                     break;
427                                 case 3: params.display = InsetGraphicsParams::COLOR; 
428                                     break;
429                                 }
430                 } else if (token == "subfigure") {
431                         params.subcaption = true;
432                 } else if (token == "width") {
433                     if (lex.next()) {
434                         int i = lex.getInteger();
435                         if (lex.next()) {
436                             if (i == 5) {
437                                 params.scale = lex.getInteger();
438                                 params.size_type = InsetGraphicsParams::SCALE;
439                             } else {
440                                 params.width = LyXLength(lex.getString()+oldUnits[i]);
441                                 params.size_type = InsetGraphicsParams::WH;
442                             }
443                         }
444                     }
445                 } else if (token == "height") {
446                     if (lex.next()) {
447                         int i = lex.getInteger();
448                         if (lex.next()) {
449                             params.height = LyXLength(lex.getString()+oldUnits[i]);
450                             params.size_type = InsetGraphicsParams::WH;
451                         }
452                     }
453                 }
454         }
455 }
456
457 string const InsetGraphics::createLatexOptions() const
458 {
459         // Calculate the options part of the command, we must do it to a string
460         // stream since we might have a trailing comma that we would like to remove
461         // before writing it to the output stream.
462         ostringstream options;
463         if (!params.bb.empty())
464             options << "  bb=" << strip(params.bb) << ",\n";
465         if (params.draft)
466             options << "  draft,\n";
467         if (params.clip)
468             options << "  clip,\n";
469         if (params.size_type == InsetGraphicsParams::WH) {
470             if (!params.width.zero())
471                 options << "  width=" << params.width.asLatexString() << ",\n";
472             if (!params.height.zero())
473                 options << "  height=" << params.height.asLatexString() << ",\n";
474         } else if (params.size_type == InsetGraphicsParams::SCALE) {
475             if (params.scale > 0)
476                 options << "  scale=" << double(params.scale)/100.0 << ",\n";
477         }
478         if (params.keepAspectRatio)
479             options << "  keepaspectratio,\n";
480         // Make sure it's not very close to zero, a float can be effectively
481         // zero but not exactly zero.
482         if (!lyx::float_equal(params.rotateAngle, 0, 0.001) && params.rotate) {
483             options << "  angle=" << params.rotateAngle << ",\n";
484             if (!params.rotateOrigin.empty()) {
485                 options << "  origin=" << params.rotateOrigin[0];
486                 if (contains(params.rotateOrigin,"Top"))
487                     options << 't';
488                 else if (contains(params.rotateOrigin,"Bottom"))
489                     options << 'b';
490                 else if (contains(params.rotateOrigin,"Baseline"))
491                     options << 'B';
492                 options << ",\n";
493             }
494         }
495         if (!params.special.empty())
496             options << params.special << ",\n";
497         string opts = options.str().c_str();
498         return opts.substr(0,opts.size()-2);    // delete last ",\n"
499 }
500
501 namespace {
502 string decideOutputImageFormat(string const & suffix)
503 {
504         // lyxrc.pdf_mode means:
505         // Are we creating a PDF or a PS file?
506         // (Should actually mean, are we using latex or pdflatex).      
507         lyxerr[Debug::INFO] << "decideOutput::lyxrc.pdf_mode = " << lyxrc.pdf_mode << "\n";
508         if (lyxrc.pdf_mode) {
509                 if (contains(suffix,"ps") || suffix == "pdf")
510                         return "pdf";
511                 else if (suffix == "jpg")
512                         return suffix;
513                 else
514                         return "png";
515         }
516         // If it's postscript, we always do eps.
517         lyxerr[Debug::INFO] << "decideOutput: we have PostScript mode\n";
518         if (suffix != "ps")
519             return "eps";
520         else
521             return "ps";
522 }
523
524 } // Anon. namespace
525
526 string const InsetGraphics::prepareFile(Buffer const *buf) const
527 {
528         // do_convert = Do we need to convert the file?
529         // nice = Do we create a nice version?
530         //        This is used when exporting the latex file only.
531         // if (!do_convert)
532         //   return original filename
533         // if (!nice)
534         //   convert_place = temp directory
535         //   return new filename in temp directory
536         // else
537         //   convert_place = original file directory
538         //   return original filename without the extension
539         //
540         // if it's a zipped one, than let LaTeX do the rest!!!
541         if ((zippedFile(params.filename) && params.noUnzip) || buf->niceFile) {
542             lyxerr[Debug::INFO] << "don't unzip file or export latex" 
543                     << params.filename << endl;
544             return params.filename;
545         }
546         string filename_ = params.filename;
547         if (zippedFile(filename_))
548             filename_ = unzipFile(filename_);
549         // now we have unzipped files
550         // Get the extension (format) of the original file.
551         // we handle it like a virtual one, so we can have
552         // different extensions with the same type.
553         string const extension = getExtFromContents(filename_);
554         // are we usind latex ((e)ps) or pdflatex (pdf,jpg,png)
555         string const image_target = decideOutputImageFormat(extension);
556         if (extension == image_target)          // :-)
557                 return filename_;
558 //      commented out to check if the "not exist"bug is fixed.
559 //      if (!IsFileReadable(filename_)) {       // :-(
560 //              Alert::alert(_("File") + params.filename,
561 //                         _("isn't readable or doesn't exists!"));
562 //              return filename_;
563 //      }
564         string outfile;
565         string const temp = AddName(buf->tmppath, filename_);
566         outfile = RemoveExtension(temp);
567         lyxerr[Debug::INFO] << "tempname = " << temp << "\n";
568         lyxerr[Debug::INFO] << "buf::tmppath = " << buf->tmppath << "\n";
569         lyxerr[Debug::INFO] << "filename_ = " << filename_ << "\n";
570         lyxerr[Debug::INFO] << "outfile = " << outfile << endl;
571         converters.convert(buf, filename_, outfile, extension, image_target);
572         return outfile;
573 }
574
575
576 int InsetGraphics::latex(Buffer const *buf, ostream & os,
577                          bool /*fragile*/, bool/*fs*/) const
578 {
579         // If there is no file specified, just output a message about it in
580         // the latex output.
581         if (params.filename.empty()) {
582                 os  << "\\fbox{\\rule[-0.5in]{0pt}{1in}"
583                         << _("empty figure path") << "}\n";
584                 return 1; // One end of line marker added to the stream.
585         }
586         // These variables collect all the latex code that should be before and
587         // after the actual includegraphics command.
588         string before;
589         string after;
590         // Do we want subcaptions?
591         if (params.subcaption) {
592                 before += "\\subfigure[" + params.subcaptionText + "]{";
593                 after = '}';
594         }
595         // We never use the starred form, we use the "clip" option instead.
596         before += "\\includegraphics";
597         // Write the options if there are any.
598         string const opts = createLatexOptions();
599         if (!opts.empty()) {
600                 before += ("[%\n" + opts +']');
601         }
602         // Make the filename relative to the lyx file
603         // and remove the extension so the LaTeX will use whatever is
604         // appropriate (when there are several versions in different formats)
605         string const latex_str = before + '{' + prepareFile(buf) + '}' + after;
606         os << latex_str;
607         // Return how many newlines we issued.
608         int const newlines =
609                 int(lyx::count(latex_str.begin(), latex_str.end(),'\n') + 1);
610         // lyxerr << "includegraphics: " << newlines << " lines of text"
611         //        << endl; 
612         return newlines;
613 }
614
615
616 int InsetGraphics::ascii(Buffer const *, ostream & os, int) const
617 {
618         // No graphics in ascii output. Possible to use gifscii to convert
619         // images to ascii approximation.
620         // 1. Convert file to ascii using gifscii
621         // 2. Read ascii output file and add it to the output stream.
622         // at least we send the filename
623         os << '<' << _("Graphicfile:") << params.filename << ">\n";
624         return 0;
625 }
626
627
628 int InsetGraphics::linuxdoc(Buffer const *, ostream &) const
629 {
630         // No graphics in LinuxDoc output. Should check how/what to add.
631         return 0;
632 }
633
634
635 // For explanation on inserting graphics into DocBook checkout:
636 // http://linuxdoc.org/LDP/LDP-Author-Guide/inserting-pictures.html
637 // See also the docbook guide at http://www.docbook.org/
638 int InsetGraphics::docbook(Buffer const * buf, ostream & os) const
639 {
640         // Change the path to be relative to the main file.
641         string const buffer_dir = buf->filePath();
642         string filename = RemoveExtension(
643                 MakeRelPath(params.filename, buffer_dir));
644
645         if (suffixIs(filename, ".eps"))
646                 filename.erase(filename.length() - 4);
647
648         // In DocBook v5.0, the graphic tag will be eliminated from DocBook, will 
649         // need to switch to MediaObject. However, for now this is sufficient and 
650         // easier to use.
651         os << "<graphic fileref=\"" << filename << "\"></graphic>";
652         return 0;
653 }
654
655
656 void InsetGraphics::validate(LaTeXFeatures & features) const
657 {
658         // If we have no image, we should not require anything.
659         if (params.filename.empty())
660                 return ;
661
662         features.require("graphicx");
663
664         if (params.subcaption)
665                 features.require("subfigure");
666 }
667
668
669 // Update the inset after parameters changed (read from file or changed in
670 // dialog.
671 void InsetGraphics::updateInset() const
672 {
673         GraphicsCache & gc = GraphicsCache::getInstance();
674         boost::shared_ptr<GraphicsCacheItem> temp(0);
675
676         // We do it this way so that in the face of some error, we will still
677         // be in a valid state.
678         InsetGraphicsParams::DisplayType local_display = params.display;
679         if (local_display == InsetGraphicsParams::DEFAULT) {
680                 if (lyxrc.display_graphics == "mono")
681                         local_display = InsetGraphicsParams::MONOCHROME;
682                 else if (lyxrc.display_graphics == "gray")
683                         local_display = InsetGraphicsParams::GRAYSCALE;
684                 else if (lyxrc.display_graphics == "color")
685                         local_display = InsetGraphicsParams::COLOR;
686                 else
687                         local_display = InsetGraphicsParams::NONE;
688         }
689
690         if (!params.filename.empty() && lyxrc.use_gui &&
691             local_display != InsetGraphicsParams::NONE) {
692                 temp = gc.addFile(params.filename);
693         }
694
695         // Mark the image as unloaded so that it gets updated.
696         imageLoaded = false;
697
698         cacheHandle = temp;
699 }
700
701
702 bool InsetGraphics::setParams(InsetGraphicsParams const & p)
703 {
704         // If nothing is changed, just return and say so.
705         if (params == p)
706                 return false;
707
708         // Copy the new parameters.
709         params = p;
710
711         // Update the inset with the new parameters.
712         updateInset();
713
714         // We have changed data, report it.
715         return true;
716 }
717
718
719 InsetGraphicsParams InsetGraphics::getParams() const
720 {
721         return params;
722 }
723
724
725 Inset * InsetGraphics::clone(Buffer const &, bool same_id) const
726 {
727         return new InsetGraphics(*this, same_id);
728 }
729