]> git.lyx.org Git - lyx.git/blob - src/insets/InsetInclude.cpp
* Inset: Prepare for an eventual merge of updateLabels() and addToToc()
[lyx.git] / src / insets / InsetInclude.cpp
1 /**
2  * \file InsetInclude.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Richard Heck (conversion to InsetCommand)
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetInclude.h"
15
16 #include "Buffer.h"
17 #include "buffer_funcs.h"
18 #include "BufferList.h"
19 #include "BufferParams.h"
20 #include "BufferView.h"
21 #include "Cursor.h"
22 #include "support/debug.h"
23 #include "DispatchResult.h"
24 #include "Exporter.h"
25 #include "FuncRequest.h"
26 #include "FuncStatus.h"
27 #include "support/gettext.h"
28 #include "LaTeXFeatures.h"
29 #include "LyX.h"
30 #include "LyXRC.h"
31 #include "Lexer.h"
32 #include "MetricsInfo.h"
33 #include "OutputParams.h"
34 #include "TocBackend.h"
35 #include "TextClass.h"
36
37 #include "frontends/alert.h"
38 #include "frontends/Painter.h"
39
40 #include "graphics/PreviewImage.h"
41 #include "graphics/PreviewLoader.h"
42
43 #include "insets/RenderPreview.h"
44 #include "insets/InsetListingsParams.h"
45
46 #include "support/docstream.h"
47 #include "support/ExceptionMessage.h"
48 #include "support/FileNameList.h"
49 #include "support/filetools.h"
50 #include "support/lstrings.h" // contains
51 #include "support/lyxalgo.h"
52 #include "support/convert.h"
53
54 #include <boost/bind.hpp>
55
56 using namespace std;
57 using namespace lyx::support;
58
59 namespace lyx {
60
61 namespace Alert = frontend::Alert;
62
63
64 namespace {
65
66 docstring const uniqueID()
67 {
68         static unsigned int seed = 1000;
69         return "file" + convert<docstring>(++seed);
70 }
71
72
73 /// the type of inclusion
74 enum Types {
75         INCLUDE, VERB, INPUT, VERBAST, LISTINGS, NONE
76 };
77
78
79 Types type(string const & s)
80 {
81         if (s == "input")
82                 return INPUT;
83         if (s == "verbatiminput")
84                 return VERB;
85         if (s == "verbatiminput*")
86                 return VERBAST;
87         if (s == "lstinputlisting")
88                 return LISTINGS;
89         if (s == "include")
90                 return INCLUDE;
91         return NONE;
92 }
93
94
95 Types type(InsetCommandParams const & params)
96 {
97         return type(params.getCmdName());
98 }
99
100
101 bool isListings(InsetCommandParams const & params)
102 {
103         return type(params) == LISTINGS;
104 }
105
106
107 bool isVerbatim(InsetCommandParams const & params)
108 {
109         Types const t = type(params);
110         return t == VERB || t == VERBAST;
111 }
112
113
114 bool isInputOrInclude(InsetCommandParams const & params)
115 {
116         Types const t = type(params);
117         return t == INPUT || t == INCLUDE;
118 }
119
120
121 FileName const masterFileName(Buffer const & buffer)
122 {
123         return buffer.masterBuffer()->fileName();
124 }
125
126
127 void add_preview(RenderMonitoredPreview &, InsetInclude const &, Buffer const &);
128
129
130 string const parentFilename(Buffer const & buffer)
131 {
132         return buffer.absFileName();
133 }
134
135
136 EmbeddedFile const includedFilename(Buffer const & buffer,
137                               InsetCommandParams const & params)
138 {
139         // it is not a good idea to create this EmbeddedFile object
140         // each time, but there seems to be no easy way around.
141         EmbeddedFile file(to_utf8(params["filename"]),
142                onlyPath(parentFilename(buffer)));
143         file.setEmbed(!params["embed"].empty());
144         file.enable(buffer.embedded(), &buffer);
145         return file;
146 }
147
148 } // namespace anon
149
150
151 InsetInclude::InsetInclude(InsetCommandParams const & p)
152         : InsetCommand(p, "include"), include_label(uniqueID()),
153           preview_(new RenderMonitoredPreview(this)), set_label_(false)
154 {
155         preview_->fileChanged(boost::bind(&InsetInclude::fileChanged, this));
156 }
157
158
159 InsetInclude::InsetInclude(InsetInclude const & other)
160         : InsetCommand(other), include_label(other.include_label),
161           preview_(new RenderMonitoredPreview(this)), set_label_(false)
162 {
163         preview_->fileChanged(boost::bind(&InsetInclude::fileChanged, this));
164 }
165
166
167 CommandInfo const * InsetInclude::findInfo(string const & /* cmdName */)
168 {
169         // FIXME
170         // This is only correct for the case of listings, but it'll do for now.
171         // In the other cases, this second parameter should just be empty.
172         static const char * const paramnames[] = {"filename", "embed", "lstparams", ""};
173         static const bool isoptional[] = {false, false, true};
174         static const CommandInfo info = {3, paramnames, isoptional};
175         return &info;
176 }
177
178
179 bool InsetInclude::isCompatibleCommand(string const & s)
180 {
181         return type(s) != NONE;
182 }
183
184
185 void InsetInclude::doDispatch(Cursor & cur, FuncRequest & cmd)
186 {
187         switch (cmd.action) {
188
189         case LFUN_INSET_MODIFY: {
190                 InsetCommandParams p(INCLUDE_CODE);
191                 InsetCommandMailer::string2params("include", to_utf8(cmd.argument()), p);
192                 if (!p.getCmdName().empty()) {
193                         if (isListings(p)){
194                                 InsetListingsParams par_old(to_utf8(params()["lstparams"]));
195                                 InsetListingsParams par_new(to_utf8(p["lstparams"]));
196                                 if (par_old.getParamValue("label") !=
197                                     par_new.getParamValue("label")
198                                     && !par_new.getParamValue("label").empty())
199                                         cur.bv().buffer().changeRefsIfUnique(
200                                                 from_utf8(par_old.getParamValue("label")),
201                                                 from_utf8(par_new.getParamValue("label")),
202                                                 REF_CODE);
203                         }
204                         try {
205                                 // the embed parameter passed back from the dialog
206                                 // is "true" or "false", we need to change it.
207                                 if (p["embed"] == _("false"))
208                                         p["embed"].clear();
209                                 else
210                                         p["embed"] = from_utf8(EmbeddedFile(to_utf8(p["filename"]),
211                                                 onlyPath(parentFilename(cur.buffer()))).inzipName());
212                                 // test parameter
213                                 includedFilename(cur.buffer(), p);
214                         } catch (ExceptionMessage const & message) {
215                                 Alert::error(message.title_, message.details_);
216                                 // do not set parameter if an error happens
217                                 break;
218                         }
219                         set(p, cur.buffer());
220                         cur.buffer().updateBibfilesCache();
221                 } else
222                         cur.noUpdate();
223                 break;
224         }
225
226         //pass everything else up the chain
227         default:
228                 InsetCommand::doDispatch(cur, cmd);
229                 break;
230         }
231 }
232
233
234 void InsetInclude::set(InsetCommandParams const & p, Buffer const & buffer)
235 {
236         setParams(p);
237         set_label_ = false;
238
239         if (preview_->monitoring())
240                 preview_->stopMonitoring();
241
242         if (type(params()) == INPUT)
243                 add_preview(*preview_, *this, buffer);
244 }
245
246
247 Inset * InsetInclude::clone() const
248 {
249         return new InsetInclude(*this);
250 }
251
252
253 docstring const InsetInclude::getScreenLabel(Buffer const & buf) const
254 {
255         docstring temp;
256
257         switch (type(params())) {
258                 case INPUT:
259                         temp = buf.B_("Input");
260                         break;
261                 case VERB:
262                         temp = buf.B_("Verbatim Input");
263                         break;
264                 case VERBAST:
265                         temp = buf.B_("Verbatim Input*");
266                         break;
267                 case INCLUDE:
268                         temp = buf.B_("Include");
269                         break;
270                 case LISTINGS:
271                         temp = listings_label_;
272                         break;
273                 case NONE:
274                         BOOST_ASSERT(false);
275         }
276
277         temp += ": ";
278
279         if (params()["filename"].empty())
280                 temp += "???";
281         else
282                 temp += from_utf8(onlyFilename(to_utf8(params()["filename"])));
283
284         if (!params()["embed"].empty())
285                 temp += _(" (embedded)");
286         return temp;
287 }
288
289
290         /// return the child buffer if the file is a LyX doc and is loaded
291 Buffer * getChildBuffer(Buffer const & buffer, InsetCommandParams const & params)
292 {
293         if (isVerbatim(params) || isListings(params))
294                 return 0;
295
296         string const included_file = includedFilename(buffer, params).absFilename();
297         if (!isLyXFilename(included_file))
298                 return 0;
299
300         Buffer * childBuffer = theBufferList().getBuffer(included_file);
301
302         //FIXME RECURSIVE INCLUDES
303         if (childBuffer == & buffer)
304                 return 0;
305         else
306                 return childBuffer;
307 }
308
309 /// return true if the file is or got loaded.
310 Buffer * loadIfNeeded(Buffer const & parent, InsetCommandParams const & params)
311 {
312         if (isVerbatim(params) || isListings(params))
313                 return 0;
314
315         string const parent_filename = parent.absFileName();
316         FileName const included_file = makeAbsPath(to_utf8(params["filename"]),
317                            onlyPath(parent_filename));
318
319         if (!isLyXFilename(included_file.absFilename()))
320                 return 0;
321
322         Buffer * child = theBufferList().getBuffer(included_file.absFilename());
323         if (!child) {
324                 // the readonly flag can/will be wrong, not anymore I think.
325                 if (!included_file.exists())
326                         return 0;
327
328                 child = theBufferList().newBuffer(included_file.absFilename());
329                 if (!child)
330                         // Buffer creation is not possible.
331                         return 0;
332
333                 if (!child->loadLyXFile(included_file)) {
334                         //close the buffer we just opened
335                         theBufferList().release(child);
336                         return 0;
337                 }
338         }
339         child->setParent(&parent);
340         return child;
341 }
342
343
344 void resetParentBuffer(Buffer const * parent, InsetCommandParams const & params,
345         bool close_it)
346 {
347         if (isVerbatim(params) || isListings(params))
348                 return;
349
350         string const parent_filename = parent->absFileName();
351         FileName const included_file = makeAbsPath(to_utf8(params["filename"]),
352                            onlyPath(parent_filename));
353
354         if (!isLyXFilename(included_file.absFilename()))
355                 return;
356
357         Buffer * child = theBufferList().getBuffer(included_file.absFilename());
358         // File not opened, nothing to close.
359         if (!child)
360                 return;
361
362         // Child document has a different parent, don't close it.
363         if (child->parent() != parent)
364                 return;
365
366         //close the buffer.
367         child->setParent(0);
368         if (close_it)
369                 theBufferList().release(child);
370         else
371                 updateLabels(*child);
372 }
373
374
375 int InsetInclude::latex(Buffer const & buffer, odocstream & os,
376                         OutputParams const & runparams) const
377 {
378         string incfile(to_utf8(params()["filename"]));
379
380         // Do nothing if no file name has been specified
381         if (incfile.empty())
382                 return 0;
383
384         FileName const included_file = includedFilename(buffer, params()).availableFile();
385
386         //Check we're not trying to include ourselves.
387         //FIXME RECURSIVE INCLUDE
388         //This isn't sufficient, as the inclusion could be downstream.
389         //But it'll have to do for now.
390         if (isInputOrInclude(params()) &&
391                 buffer.absFileName() == included_file.absFilename())
392         {
393                 Alert::error(_("Recursive input"),
394                                bformat(_("Attempted to include file %1$s in itself! "
395                                "Ignoring inclusion."), from_utf8(incfile)));
396                 return 0;
397         }
398
399         Buffer const * const masterBuffer = buffer.masterBuffer();
400
401         // if incfile is relative, make it relative to the master
402         // buffer directory.
403         if (!FileName(incfile).isAbsolute()) {
404                 // FIXME UNICODE
405                 incfile = to_utf8(makeRelPath(from_utf8(included_file.absFilename()),
406                                               from_utf8(masterBuffer->filePath())));
407         }
408
409         // write it to a file (so far the complete file)
410         string const exportfile = changeExtension(incfile, ".tex");
411         string const mangled =
412                 DocFileName(changeExtension(included_file.absFilename(),".tex")).
413                         mangledFilename();
414         FileName const writefile(makeAbsPath(mangled, masterBuffer->temppath()));
415
416         if (!runparams.nice)
417                 incfile = mangled;
418         else if (!isValidLaTeXFilename(incfile)) {
419                 frontend::Alert::warning(_("Invalid filename"),
420                                          _("The following filename is likely to cause trouble "
421                                            "when running the exported file through LaTeX: ") +
422                                             from_utf8(incfile));
423         }
424         LYXERR(Debug::LATEX, "incfile:" << incfile);
425         LYXERR(Debug::LATEX, "exportfile:" << exportfile);
426         LYXERR(Debug::LATEX, "writefile:" << writefile);
427
428         if (runparams.inComment || runparams.dryrun) {
429                 //Don't try to load or copy the file if we're
430                 //in a comment or doing a dryrun
431         } else if (isInputOrInclude(params()) &&
432                  isLyXFilename(included_file.absFilename())) {
433                 //if it's a LyX file and we're inputting or including,
434                 //try to load it so we can write the associated latex
435                 if (!loadIfNeeded(buffer, params()))
436                         return false;
437
438                 Buffer * tmp = theBufferList().getBuffer(included_file.absFilename());
439
440                 if (tmp->params().getBaseClass() != masterBuffer->params().getBaseClass()) {
441                         // FIXME UNICODE
442                         docstring text = bformat(_("Included file `%1$s'\n"
443                                                 "has textclass `%2$s'\n"
444                                                              "while parent file has textclass `%3$s'."),
445                                               included_file.displayName(),
446                                               from_utf8(tmp->params().getTextClass().name()),
447                                               from_utf8(masterBuffer->params().getTextClass().name()));
448                         Alert::warning(_("Different textclasses"), text);
449                         //return 0;
450                 }
451
452                 // Make sure modules used in child are all included in master
453                 //FIXME It might be worth loading the children's modules into the master
454                 //over in BufferParams rather than doing this check.
455                 vector<string> const masterModules = masterBuffer->params().getModules();
456                 vector<string> const childModules = tmp->params().getModules();
457                 vector<string>::const_iterator it = childModules.begin();
458                 vector<string>::const_iterator end = childModules.end();
459                 for (; it != end; ++it) {
460                         string const module = *it;
461                         vector<string>::const_iterator found =
462                                 find(masterModules.begin(), masterModules.end(), module);
463                         if (found != masterModules.end()) {
464                                 docstring text = bformat(_("Included file `%1$s'\n"
465                                                         "uses module `%2$s'\n"
466                                                         "which is not used in parent file."),
467                                        included_file.displayName(), from_utf8(module));
468                                 Alert::warning(_("Module not found"), text);
469                         }
470                 }
471
472                 tmp->markDepClean(masterBuffer->temppath());
473
474 // FIXME: handle non existing files
475 // FIXME: Second argument is irrelevant!
476 // since only_body is true, makeLaTeXFile will not look at second
477 // argument. Should we set it to string(), or should makeLaTeXFile
478 // make use of it somehow? (JMarc 20031002)
479                 // The included file might be written in a different encoding
480                 Encoding const * const oldEnc = runparams.encoding;
481                 runparams.encoding = &tmp->params().encoding();
482                 tmp->makeLaTeXFile(writefile,
483                                    masterFileName(buffer).onlyPath().absFilename(),
484                                    runparams, false);
485                 runparams.encoding = oldEnc;
486         } else {
487                 // In this case, it's not a LyX file, so we copy the file
488                 // to the temp dir, so that .aux files etc. are not created
489                 // in the original dir. Files included by this file will be
490                 // found via input@path, see ../Buffer.cpp.
491                 unsigned long const checksum_in  = included_file.checksum();
492                 unsigned long const checksum_out = writefile.checksum();
493
494                 if (checksum_in != checksum_out) {
495                         if (!included_file.copyTo(writefile)) {
496                                 // FIXME UNICODE
497                                 LYXERR(Debug::LATEX,
498                                         to_utf8(bformat(_("Could not copy the file\n%1$s\n"
499                                                                   "into the temporary directory."),
500                                                    from_utf8(included_file.absFilename()))));
501                                 return 0;
502                         }
503                 }
504         }
505
506         string const tex_format = (runparams.flavor == OutputParams::LATEX) ?
507                         "latex" : "pdflatex";
508         if (isVerbatim(params())) {
509                 incfile = latex_path(incfile);
510                 // FIXME UNICODE
511                 os << '\\' << from_ascii(params().getCmdName()) << '{'
512                    << from_utf8(incfile) << '}';
513         } else if (type(params()) == INPUT) {
514                 runparams.exportdata->addExternalFile(tex_format, writefile,
515                                                       exportfile);
516
517                 // \input wants file with extension (default is .tex)
518                 if (!isLyXFilename(included_file.absFilename())) {
519                         incfile = latex_path(incfile);
520                         // FIXME UNICODE
521                         os << '\\' << from_ascii(params().getCmdName())
522                            << '{' << from_utf8(incfile) << '}';
523                 } else {
524                 incfile = changeExtension(incfile, ".tex");
525                 incfile = latex_path(incfile);
526                         // FIXME UNICODE
527                         os << '\\' << from_ascii(params().getCmdName())
528                            << '{' << from_utf8(incfile) <<  '}';
529                 }
530         } else if (type(params()) == LISTINGS) {
531                 os << '\\' << from_ascii(params().getCmdName());
532                 string const opt = to_utf8(params()["lstparams"]);
533                 // opt is set in QInclude dialog and should have passed validation.
534                 InsetListingsParams params(opt);
535                 if (!params.params().empty())
536                         os << "[" << from_utf8(params.params()) << "]";
537                 os << '{'  << from_utf8(incfile) << '}';
538         } else {
539                 runparams.exportdata->addExternalFile(tex_format, writefile,
540                                                       exportfile);
541
542                 // \include don't want extension and demands that the
543                 // file really have .tex
544                 incfile = changeExtension(incfile, string());
545                 incfile = latex_path(incfile);
546                 // FIXME UNICODE
547                 os << '\\' << from_ascii(params().getCmdName()) << '{'
548                    << from_utf8(incfile) << '}';
549         }
550
551         return 0;
552 }
553
554
555 int InsetInclude::plaintext(Buffer const & buffer, odocstream & os,
556                             OutputParams const &) const
557 {
558         if (isVerbatim(params()) || isListings(params())) {
559                 os << '[' << getScreenLabel(buffer) << '\n';
560                 // FIXME: We don't know the encoding of the file, default to UTF-8.
561                 os << includedFilename(buffer, params()).fileContents("UTF-8");
562                 os << "\n]";
563                 return PLAINTEXT_NEWLINE + 1; // one char on a separate line
564         } else {
565                 docstring const str = '[' + getScreenLabel(buffer) + ']';
566                 os << str;
567                 return str.size();
568         }
569 }
570
571
572 int InsetInclude::docbook(Buffer const & buffer, odocstream & os,
573                           OutputParams const & runparams) const
574 {
575         string incfile = to_utf8(params()["filename"]);
576
577         // Do nothing if no file name has been specified
578         if (incfile.empty())
579                 return 0;
580
581         string const included_file = includedFilename(buffer, params()).absFilename();
582
583         //Check we're not trying to include ourselves.
584         //FIXME RECURSIVE INCLUDE
585         //This isn't sufficient, as the inclusion could be downstream.
586         //But it'll have to do for now.
587         if (buffer.absFileName() == included_file) {
588                 Alert::error(_("Recursive input"),
589                                bformat(_("Attempted to include file %1$s in itself! "
590                                "Ignoring inclusion."), from_utf8(incfile)));
591                 return 0;
592         }
593
594         // write it to a file (so far the complete file)
595         string const exportfile = changeExtension(incfile, ".sgml");
596         DocFileName writefile(changeExtension(included_file, ".sgml"));
597
598         if (loadIfNeeded(buffer, params())) {
599                 Buffer * tmp = theBufferList().getBuffer(included_file);
600
601                 string const mangled = writefile.mangledFilename();
602                 writefile = makeAbsPath(mangled,
603                                         buffer.masterBuffer()->temppath());
604                 if (!runparams.nice)
605                         incfile = mangled;
606
607                 LYXERR(Debug::LATEX, "incfile:" << incfile);
608                 LYXERR(Debug::LATEX, "exportfile:" << exportfile);
609                 LYXERR(Debug::LATEX, "writefile:" << writefile);
610
611                 tmp->makeDocBookFile(writefile, runparams, true);
612         }
613
614         runparams.exportdata->addExternalFile("docbook", writefile,
615                                               exportfile);
616         runparams.exportdata->addExternalFile("docbook-xml", writefile,
617                                               exportfile);
618
619         if (isVerbatim(params()) || isListings(params())) {
620                 os << "<inlinegraphic fileref=\""
621                    << '&' << include_label << ';'
622                    << "\" format=\"linespecific\">";
623         } else
624                 os << '&' << include_label << ';';
625
626         return 0;
627 }
628
629
630 void InsetInclude::validate(LaTeXFeatures & features) const
631 {
632         string incfile = to_utf8(params()["filename"]);
633         string writefile;
634
635         Buffer const & buffer = features.buffer();
636
637         string const included_file = includedFilename(buffer, params()).availableFile().absFilename();
638
639         if (isLyXFilename(included_file))
640                 writefile = changeExtension(included_file, ".sgml");
641         else
642                 writefile = included_file;
643
644         if (!features.runparams().nice && !isVerbatim(params()) && !isListings(params())) {
645                 incfile = DocFileName(writefile).mangledFilename();
646                 writefile = makeAbsPath(incfile,
647                                         buffer.masterBuffer()->temppath()).absFilename();
648         }
649
650         features.includeFile(include_label, writefile);
651
652         if (isVerbatim(params()))
653                 features.require("verbatim");
654         else if (isListings(params()))
655                 features.require("listings");
656
657         // Here we must do the fun stuff...
658         // Load the file in the include if it needs
659         // to be loaded:
660         if (loadIfNeeded(buffer, params())) {
661                 // a file got loaded
662                 Buffer * const tmp = theBufferList().getBuffer(included_file);
663                 // make sure the buffer isn't us
664                 // FIXME RECURSIVE INCLUDES
665                 // This is not sufficient, as recursive includes could be
666                 // more than a file away. But it will do for now.
667                 if (tmp && tmp != & buffer) {
668                         // We must temporarily change features.buffer,
669                         // otherwise it would always be the master buffer,
670                         // and nested includes would not work.
671                         features.setBuffer(*tmp);
672                         tmp->validate(features);
673                         features.setBuffer(buffer);
674                 }
675         }
676 }
677
678
679 void InsetInclude::getLabelList(Buffer const & buffer,
680                                 vector<docstring> & list) const
681 {
682         if (isListings(params())) {
683                 InsetListingsParams p(to_utf8(params()["lstparams"]));
684                 string label = p.getParamValue("label");
685                 if (!label.empty())
686                         list.push_back(from_utf8(label));
687         }
688         else if (loadIfNeeded(buffer, params())) {
689                 string const included_file = includedFilename(buffer, params()).absFilename();
690                 Buffer * tmp = theBufferList().getBuffer(included_file);
691                 tmp->setParent(0);
692                 tmp->getLabelList(list);
693                 tmp->setParent(const_cast<Buffer *>(&buffer));
694         }
695 }
696
697
698 void InsetInclude::fillWithBibKeys(Buffer const & buffer,
699                 BiblioInfo & keys, InsetIterator const & /*di*/) const
700 {
701         if (loadIfNeeded(buffer, params())) {
702                 string const included_file = includedFilename(buffer, params()).absFilename();
703                 Buffer * tmp = theBufferList().getBuffer(included_file);
704                 //FIXME This is kind of a dirty hack and should be made reasonable.
705                 tmp->setParent(0);
706                 keys.fillWithBibKeys(tmp);
707                 tmp->setParent(&buffer);
708         }
709 }
710
711
712 void InsetInclude::updateBibfilesCache(Buffer const & buffer)
713 {
714         Buffer * const tmp = getChildBuffer(buffer, params());
715         if (tmp) {
716                 tmp->setParent(0);
717                 tmp->updateBibfilesCache();
718                 tmp->setParent(&buffer);
719         }
720 }
721
722
723 EmbeddedFileList const &
724 InsetInclude::getBibfilesCache(Buffer const & buffer) const
725 {
726         Buffer * const tmp = getChildBuffer(buffer, params());
727         if (tmp) {
728                 tmp->setParent(0);
729                 EmbeddedFileList const & cache = tmp->getBibfilesCache();
730                 tmp->setParent(&buffer);
731                 return cache;
732         }
733         static EmbeddedFileList const empty;
734         return empty;
735 }
736
737
738 void InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
739 {
740         BOOST_ASSERT(mi.base.bv);
741
742         bool use_preview = false;
743         if (RenderPreview::status() != LyXRC::PREVIEW_OFF) {
744                 graphics::PreviewImage const * pimage =
745                         preview_->getPreviewImage(mi.base.bv->buffer());
746                 use_preview = pimage && pimage->image();
747         }
748
749         if (use_preview) {
750                 preview_->metrics(mi, dim);
751         } else {
752                 if (!set_label_) {
753                         set_label_ = true;
754                         button_.update(getScreenLabel(mi.base.bv->buffer()),
755                                        true);
756                 }
757                 button_.metrics(mi, dim);
758         }
759
760         Box b(0, dim.wid, -dim.asc, dim.des);
761         button_.setBox(b);
762 }
763
764
765 void InsetInclude::draw(PainterInfo & pi, int x, int y) const
766 {
767         BOOST_ASSERT(pi.base.bv);
768
769         bool use_preview = false;
770         if (RenderPreview::status() != LyXRC::PREVIEW_OFF) {
771                 graphics::PreviewImage const * pimage =
772                         preview_->getPreviewImage(pi.base.bv->buffer());
773                 use_preview = pimage && pimage->image();
774         }
775
776         if (use_preview)
777                 preview_->draw(pi, x, y);
778         else
779                 button_.draw(pi, x, y);
780 }
781
782
783 Inset::DisplayType InsetInclude::display() const
784 {
785         return type(params()) == INPUT ? Inline : AlignCenter;
786 }
787
788
789
790 //
791 // preview stuff
792 //
793
794 void InsetInclude::fileChanged() const
795 {
796         Buffer const * const buffer = updateFrontend();
797         if (!buffer)
798                 return;
799
800         preview_->removePreview(*buffer);
801         add_preview(*preview_.get(), *this, *buffer);
802         preview_->startLoading(*buffer);
803 }
804
805
806 namespace {
807
808 bool preview_wanted(InsetCommandParams const & params, Buffer const & buffer)
809 {
810         FileName const included_file = includedFilename(buffer, params);
811
812         return type(params) == INPUT && params.preview() &&
813                 included_file.isReadableFile();
814 }
815
816
817 docstring const latex_string(InsetInclude const & inset, Buffer const & buffer)
818 {
819         odocstringstream os;
820         // We don't need to set runparams.encoding since this will be done
821         // by latex() anyway.
822         OutputParams runparams(0);
823         runparams.flavor = OutputParams::LATEX;
824         inset.latex(buffer, os, runparams);
825
826         return os.str();
827 }
828
829
830 void add_preview(RenderMonitoredPreview & renderer, InsetInclude const & inset,
831                  Buffer const & buffer)
832 {
833         InsetCommandParams const & params = inset.params();
834         if (RenderPreview::status() != LyXRC::PREVIEW_OFF &&
835             preview_wanted(params, buffer)) {
836                 renderer.setAbsFile(includedFilename(buffer, params));
837                 docstring const snippet = latex_string(inset, buffer);
838                 renderer.addPreview(snippet, buffer);
839         }
840 }
841
842 } // namespace anon
843
844
845 void InsetInclude::addPreview(graphics::PreviewLoader & ploader) const
846 {
847         Buffer const & buffer = ploader.buffer();
848         if (preview_wanted(params(), buffer)) {
849                 preview_->setAbsFile(includedFilename(buffer, params()));
850                 docstring const snippet = latex_string(*this, buffer);
851                 preview_->addPreview(snippet, ploader);
852         }
853 }
854
855
856 void InsetInclude::addToToc(Buffer const & buffer,
857         ParConstIterator const & cpit) const
858 {
859         if (isListings(params())) {
860                 InsetListingsParams p(to_utf8(params()["lstparams"]));
861                 string caption = p.getParamValue("caption");
862                 if (caption.empty())
863                         return;
864                 Toc & toc = buffer.tocBackend().toc("listing");
865                 docstring const str = convert<docstring>(toc.size() + 1)
866                         + ". " +  from_utf8(caption);
867                 ParConstIterator pit = cpit;
868                 pit.push_back(*this);
869                 toc.push_back(TocItem(pit, 0, str));
870                 return;
871         }
872         Buffer const * const childbuffer = getChildBuffer(buffer, params());
873         if (!childbuffer)
874                 return;
875
876         TocList & toclist = buffer.tocBackend().tocs();
877         TocList const & childtoclist = childbuffer->tocBackend().tocs();
878         TocList::const_iterator it = childtoclist.begin();
879         TocList::const_iterator const end = childtoclist.end();
880         for(; it != end; ++it)
881                 toclist[it->first].insert(toclist[it->first].end(),
882                                 it->second.begin(), it->second.end());
883 }
884
885
886 void InsetInclude::updateLabels(Buffer const & buffer, ParIterator const &)
887 {
888         Buffer const * const childbuffer = getChildBuffer(buffer, params());
889         if (childbuffer)
890                 lyx::updateLabels(*childbuffer, true);
891         else if (isListings(params())) {
892                 InsetListingsParams const par(to_utf8(params()["lstparams"]));
893                 if (par.getParamValue("caption").empty())
894                         listings_label_.clear();
895                 else {
896                         Counters & counters = buffer.params().getTextClass().counters();
897                         docstring const cnt = from_ascii("listing");
898                         if (counters.hasCounter(cnt)) {
899                                 counters.step(cnt);
900                                 listings_label_ = buffer.B_("Program Listing ")
901                                         + convert<docstring>(counters.value(cnt));
902                         } else
903                                 listings_label_ = buffer.B_("Program Listing");
904                 }
905         }
906 }
907
908
909 void InsetInclude::registerEmbeddedFiles(Buffer const & buffer,
910         EmbeddedFileList & files) const
911 {
912         files.registerFile(includedFilename(buffer, params()), this, buffer);
913 }
914
915
916 void InsetInclude::updateEmbeddedFile(Buffer const & buf,
917         EmbeddedFile const & file)
918 {
919         InsetCommandParams p = params();
920         p["filename"] = from_utf8(file.outputFilename());
921         p["embed"] = file.embedded() ? from_utf8(file.inzipName()) : docstring();
922         set(p, buf);
923 }
924
925
926 } // namespace lyx