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