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