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