]> git.lyx.org Git - lyx.git/blob - src/insets/InsetInclude.cpp
Increase listings counter for InsetInclude with lstinputlisting and a caption, fix...
[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), counter_(0)
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), counter_(0)
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                         if (counter_ > 0)
341                                 temp += buf.B_("Program Listing ") + convert<docstring>(counter_);
342                         else
343                                 temp += buf.B_("Program Listing");
344                         break;
345                 }
346         }
347
348         temp += ": ";
349
350         if (params_["filename"].empty())
351                 temp += "???";
352         else
353                 temp += from_utf8(onlyFilename(to_utf8(params_["filename"])));
354
355         return temp;
356 }
357
358
359 namespace {
360
361 /// return the child buffer if the file is a LyX doc and is loaded
362 Buffer * getChildBuffer(Buffer const & buffer, InsetCommandParams const & params)
363 {
364         if (isVerbatim(params) || isListings(params))
365                 return 0;
366
367         string const included_file = includedFilename(buffer, params).absFilename();
368         if (!isLyXFilename(included_file))
369                 return 0;
370
371         Buffer * childBuffer = theBufferList().getBuffer(included_file);
372         
373         //FIXME RECURSIVE INCLUDES
374         if (childBuffer == & buffer)
375                 return 0;
376         else return childBuffer;
377 }
378
379
380 /// return true if the file is or got loaded.
381 bool loadIfNeeded(Buffer const & buffer, InsetCommandParams const & params)
382 {
383         if (isVerbatim(params) || isListings(params))
384                 return false;
385
386         FileName const included_file = includedFilename(buffer, params);
387         if (!isLyXFilename(included_file.absFilename()))
388                 return false;
389
390         Buffer * buf = theBufferList().getBuffer(included_file.absFilename());
391         if (!buf) {
392                 // the readonly flag can/will be wrong, not anymore I think.
393                 if (!fs::exists(included_file.toFilesystemEncoding()))
394                         return false;
395                 buf = theBufferList().newBuffer(included_file.absFilename());
396                 if (!loadLyXFile(buf, included_file))
397                         return false;
398         }
399         if (buf)
400                 buf->setParentName(parentFilename(buffer));
401         return buf != 0;
402 }
403
404
405 } // namespace anon
406
407
408 int InsetInclude::latex(Buffer const & buffer, odocstream & os,
409                         OutputParams const & runparams) const
410 {
411         string incfile(to_utf8(params_["filename"]));
412
413         // Do nothing if no file name has been specified
414         if (incfile.empty())
415                 return 0;
416
417         FileName const included_file(includedFilename(buffer, params_));
418         
419         //Check we're not trying to include ourselves.
420         //FIXME RECURSIVE INCLUDE
421         //This isn't sufficient, as the inclusion could be downstream.
422         //But it'll have to do for now.
423         if (!isListings(params_) && buffer.fileName() == included_file.toFilesystemEncoding()) {
424                 Alert::error(_("Recursive input"), 
425                                bformat(_("Attempted to include file %1$s in itself! "
426                                "Ignoring inclusion."), from_utf8(incfile)));
427                 return 0;
428         }
429
430         Buffer const * const m_buffer = buffer.getMasterBuffer();
431
432         // if incfile is relative, make it relative to the master
433         // buffer directory.
434         if (!absolutePath(incfile)) {
435                 // FIXME UNICODE
436                 incfile = to_utf8(makeRelPath(from_utf8(included_file.absFilename()),
437                                               from_utf8(m_buffer->filePath())));
438         }
439
440         // write it to a file (so far the complete file)
441         string const exportfile = changeExtension(incfile, ".tex");
442         string const mangled = DocFileName(changeExtension(included_file.absFilename(),
443                                                         ".tex")).mangledFilename();
444         FileName const writefile(makeAbsPath(mangled, m_buffer->temppath()));
445
446         if (!runparams.nice)
447                 incfile = mangled;
448         LYXERR(Debug::LATEX) << "incfile:" << incfile << endl;
449         LYXERR(Debug::LATEX) << "exportfile:" << exportfile << endl;
450         LYXERR(Debug::LATEX) << "writefile:" << writefile << endl;
451
452         if (runparams.inComment || runparams.dryrun)
453                 // Don't try to load or copy the file
454                 ;
455         else if (loadIfNeeded(buffer, params_)) {
456                 Buffer * tmp = theBufferList().getBuffer(included_file.absFilename());
457
458                 if (tmp->params().textclass != m_buffer->params().textclass) {
459                         // FIXME UNICODE
460                         docstring text = bformat(_("Included file `%1$s'\n"
461                                                 "has textclass `%2$s'\n"
462                                                              "while parent file has textclass `%3$s'."),
463                                               makeDisplayPath(included_file.absFilename()),
464                                               from_utf8(tmp->params().getTextClass().name()),
465                                               from_utf8(m_buffer->params().getTextClass().name()));
466                         Alert::warning(_("Different textclasses"), text);
467                         //return 0;
468                 }
469
470                 tmp->markDepClean(m_buffer->temppath());
471
472 #ifdef WITH_WARNINGS
473 #warning handle non existing files
474 #warning Second argument is irrelevant!
475 // since only_body is true, makeLaTeXFile will not look at second
476 // argument. Should we set it to string(), or should makeLaTeXFile
477 // make use of it somehow? (JMarc 20031002)
478 #endif
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                                    onlyPath(masterFilename(buffer)),
484                                    runparams, false);
485                 runparams.encoding = oldEnc;
486         } else {
487                 // Copy the file to the temp dir, so that .aux files etc.
488                 // are not created in the original dir. Files included by
489                 // this file will be found via input@path, see ../Buffer.cpp.
490                 unsigned long const checksum_in  = sum(included_file);
491                 unsigned long const checksum_out = sum(writefile);
492
493                 if (checksum_in != checksum_out) {
494                         if (!copy(included_file, writefile)) {
495                                 // FIXME UNICODE
496                                 LYXERR(Debug::LATEX)
497                                         << to_utf8(bformat(_("Could not copy the file\n%1$s\n"
498                                                                   "into the temporary directory."),
499                                                    from_utf8(included_file.absFilename())))
500                                         << endl;
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 opt = params_.getOptions();
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.encodedString()) << "]";
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
561                 docstring const str =
562                      from_utf8(getFileContents(includedFilename(buffer, params_)));
563                 os << str;
564                 os << "\n]";
565                 return PLAINTEXT_NEWLINE + 1; // one char on a separate line
566         } else {
567                 docstring const str = '[' + getScreenLabel(buffer) + ']';
568                 os << str;
569                 return str.size();
570         }
571 }
572
573
574 int InsetInclude::docbook(Buffer const & buffer, odocstream & os,
575                           OutputParams const & runparams) const
576 {
577         string incfile = to_utf8(params_["filename"]);
578
579         // Do nothing if no file name has been specified
580         if (incfile.empty())
581                 return 0;
582
583         string const included_file = includedFilename(buffer, params_).absFilename();
584
585         //Check we're not trying to include ourselves.
586         //FIXME RECURSIVE INCLUDE
587         //This isn't sufficient, as the inclusion could be downstream.
588         //But it'll have to do for now.
589         if (buffer.fileName() == included_file) {
590                 Alert::error(_("Recursive input"), 
591                                bformat(_("Attempted to include file %1$s in itself! "
592                                "Ignoring inclusion."), from_utf8(incfile)));
593                 return 0;
594         }
595
596         // write it to a file (so far the complete file)
597         string const exportfile = changeExtension(incfile, ".sgml");
598         DocFileName writefile(changeExtension(included_file, ".sgml"));
599
600         if (loadIfNeeded(buffer, params_)) {
601                 Buffer * tmp = theBufferList().getBuffer(included_file);
602
603                 string const mangled = writefile.mangledFilename();
604                 writefile = makeAbsPath(mangled,
605                                         buffer.getMasterBuffer()->temppath());
606                 if (!runparams.nice)
607                         incfile = mangled;
608
609                 LYXERR(Debug::LATEX) << "incfile:" << incfile << endl;
610                 LYXERR(Debug::LATEX) << "exportfile:" << exportfile << endl;
611                 LYXERR(Debug::LATEX) << "writefile:" << writefile << endl;
612
613                 tmp->makeDocBookFile(writefile, runparams, true);
614         }
615
616         runparams.exportdata->addExternalFile("docbook", writefile,
617                                               exportfile);
618         runparams.exportdata->addExternalFile("docbook-xml", writefile,
619                                               exportfile);
620
621         if (isVerbatim(params_) || isListings(params_)) {
622                 os << "<inlinegraphic fileref=\""
623                    << '&' << include_label << ';'
624                    << "\" format=\"linespecific\">";
625         } else
626                 os << '&' << include_label << ';';
627
628         return 0;
629 }
630
631
632 void InsetInclude::validate(LaTeXFeatures & features) const
633 {
634         string incfile(to_utf8(params_["filename"]));
635         string writefile;
636
637         Buffer const & buffer = features.buffer();
638
639         string const included_file = includedFilename(buffer, params_).absFilename();
640
641         if (isLyXFilename(included_file))
642                 writefile = changeExtension(included_file, ".sgml");
643         else
644                 writefile = included_file;
645
646         if (!features.runparams().nice && !isVerbatim(params_) && !isListings(params_)) {
647                 incfile = DocFileName(writefile).mangledFilename();
648                 writefile = makeAbsPath(incfile,
649                                         buffer.getMasterBuffer()->temppath()).absFilename();
650         }
651
652         features.includeFile(include_label, writefile);
653
654         if (isVerbatim(params_))
655                 features.require("verbatim");
656         else if (isListings(params_))
657                 features.require("listings");
658
659         // Here we must do the fun stuff...
660         // Load the file in the include if it needs
661         // to be loaded:
662         if (loadIfNeeded(buffer, params_)) {
663                 // a file got loaded
664                 Buffer * const tmp = theBufferList().getBuffer(included_file);
665                 // make sure the buffer isn't us
666                 // FIXME RECURSIVE INCLUDES
667                 // This is not sufficient, as recursive includes could be
668                 // more than a file away. But it will do for now.
669                 if (tmp && tmp != & buffer) {
670                         // We must temporarily change features.buffer,
671                         // otherwise it would always be the master buffer,
672                         // and nested includes would not work.
673                         features.setBuffer(*tmp);
674                         tmp->validate(features);
675                         features.setBuffer(buffer);
676                 }
677         }
678 }
679
680
681 void InsetInclude::getLabelList(Buffer const & buffer,
682                                 std::vector<docstring> & list) const
683 {
684         if (isListings(params_)) {
685                 InsetListingsParams params(params_.getOptions());
686                 string label = params.getParamValue("label");
687                 if (!label.empty())
688                         list.push_back(from_utf8(label));
689         }
690         else if (loadIfNeeded(buffer, params_)) {
691                 string const included_file = includedFilename(buffer, params_).absFilename();
692                 Buffer * tmp = theBufferList().getBuffer(included_file);
693                 tmp->setParentName("");
694                 tmp->getLabelList(list);
695                 tmp->setParentName(parentFilename(buffer));
696         }
697 }
698
699
700 void InsetInclude::fillWithBibKeys(Buffer const & buffer,
701                 std::vector<std::pair<string, docstring> > & keys) const
702 {
703         if (loadIfNeeded(buffer, params_)) {
704                 string const included_file = includedFilename(buffer, params_).absFilename();
705                 Buffer * tmp = theBufferList().getBuffer(included_file);
706                 tmp->setParentName("");
707                 tmp->fillWithBibKeys(keys);
708                 tmp->setParentName(parentFilename(buffer));
709         }
710 }
711
712
713 void InsetInclude::updateBibfilesCache(Buffer const & buffer)
714 {
715         Buffer * const tmp = getChildBuffer(buffer, params_);
716         if (tmp) {
717                 tmp->setParentName("");
718                 tmp->updateBibfilesCache();
719                 tmp->setParentName(parentFilename(buffer));
720         }
721 }
722
723
724 std::vector<FileName> const &
725 InsetInclude::getBibfilesCache(Buffer const & buffer) const
726 {
727         Buffer * const tmp = getChildBuffer(buffer, params_);
728         if (tmp) {
729                 tmp->setParentName("");
730                 std::vector<FileName> const & cache = tmp->getBibfilesCache();
731                 tmp->setParentName(parentFilename(buffer));
732                 return cache;
733         }
734         static std::vector<FileName> const empty;
735         return empty;
736 }
737
738
739 bool InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
740 {
741         BOOST_ASSERT(mi.base.bv && mi.base.bv->buffer());
742
743         bool use_preview = false;
744         if (RenderPreview::status() != LyXRC::PREVIEW_OFF) {
745                 graphics::PreviewImage const * pimage =
746                         preview_->getPreviewImage(*mi.base.bv->buffer());
747                 use_preview = pimage && pimage->image();
748         }
749
750         if (use_preview) {
751                 preview_->metrics(mi, dim);
752         } else {
753                 if (!set_label_) {
754                         set_label_ = true;
755                         button_.update(getScreenLabel(*mi.base.bv->buffer()),
756                                        true);
757                 }
758                 button_.metrics(mi, dim);
759         }
760
761         Box b(0, dim.wid, -dim.asc, dim.des);
762         button_.setBox(b);
763
764         bool const changed = dim_ != dim;
765         dim_ = dim;
766         return changed;
767 }
768
769
770 void InsetInclude::draw(PainterInfo & pi, int x, int y) const
771 {
772         setPosCache(pi, x, y);
773
774         BOOST_ASSERT(pi.base.bv && pi.base.bv->buffer());
775
776         bool use_preview = false;
777         if (RenderPreview::status() != LyXRC::PREVIEW_OFF) {
778                 graphics::PreviewImage const * pimage =
779                         preview_->getPreviewImage(*pi.base.bv->buffer());
780                 use_preview = pimage && pimage->image();
781         }
782
783         if (use_preview)
784                 preview_->draw(pi, x, y);
785         else
786                 button_.draw(pi, x, y);
787 }
788
789
790 Inset::DisplayType InsetInclude::display() const
791 {
792         return type(params_) == INPUT ? Inline : AlignCenter;
793 }
794
795
796
797 //
798 // preview stuff
799 //
800
801 void InsetInclude::fileChanged() const
802 {
803         Buffer const * const buffer_ptr = LyX::cref().updateInset(this);
804         if (!buffer_ptr)
805                 return;
806
807         Buffer const & buffer = *buffer_ptr;
808         preview_->removePreview(buffer);
809         add_preview(*preview_.get(), *this, buffer);
810         preview_->startLoading(buffer);
811 }
812
813
814 namespace {
815
816 bool preview_wanted(InsetCommandParams const & params, Buffer const & buffer)
817 {
818         FileName const included_file = includedFilename(buffer, params);
819
820         return type(params) == INPUT && params.preview() &&
821                 isFileReadable(included_file);
822 }
823
824
825 docstring const latex_string(InsetInclude const & inset, Buffer const & buffer)
826 {
827         odocstringstream os;
828         // We don't need to set runparams.encoding since this will be done
829         // by latex() anyway.
830         OutputParams runparams(0);
831         runparams.flavor = OutputParams::LATEX;
832         inset.latex(buffer, os, runparams);
833
834         return os.str();
835 }
836
837
838 void add_preview(RenderMonitoredPreview & renderer, InsetInclude const & inset,
839                  Buffer const & buffer)
840 {
841         InsetCommandParams const & params = inset.params();
842         if (RenderPreview::status() != LyXRC::PREVIEW_OFF &&
843             preview_wanted(params, buffer)) {
844                 renderer.setAbsFile(includedFilename(buffer, params));
845                 docstring const snippet = latex_string(inset, buffer);
846                 renderer.addPreview(snippet, buffer);
847         }
848 }
849
850 } // namespace anon
851
852
853 void InsetInclude::addPreview(graphics::PreviewLoader & ploader) const
854 {
855         Buffer const & buffer = ploader.buffer();
856         if (preview_wanted(params(), buffer)) {
857                 preview_->setAbsFile(includedFilename(buffer, params()));
858                 docstring const snippet = latex_string(*this, buffer);
859                 preview_->addPreview(snippet, ploader);
860         }
861 }
862
863
864 void InsetInclude::addToToc(TocList & toclist, Buffer const & buffer) const
865 {
866         Buffer const * const childbuffer = getChildBuffer(buffer, params_);
867         if (!childbuffer)
868                 return;
869
870         TocList const & childtoclist = childbuffer->tocBackend().tocs();
871         TocList::const_iterator it = childtoclist.begin();
872         TocList::const_iterator const end = childtoclist.end();
873         for(; it != end; ++it)
874                 toclist[it->first].insert(toclist[it->first].end(),
875                                 it->second.begin(), it->second.end());
876 }
877
878
879 void InsetInclude::updateLabels(Buffer const & buffer) const
880 {
881         Buffer const * const childbuffer = getChildBuffer(buffer, params_);
882         if (!childbuffer)
883                 return;
884
885         lyx::updateLabels(*childbuffer, true);
886 }
887
888
889 void InsetInclude::updateCounter(Counters & counters)
890 {
891         if (!isListings(params_))
892                 return;
893
894         InsetListingsParams const par = params_.getOptions();
895         if (par.getParamValue("caption").empty())
896                 counter_ = 0;
897         else {
898                 counters.step(from_ascii("listing"));
899                 counter_ = counters.value(from_ascii("listing"));
900         }
901 }
902
903
904 string const InsetIncludeMailer::name_("include");
905
906 InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
907         : inset_(inset)
908 {}
909
910
911 string const InsetIncludeMailer::inset2string(Buffer const &) const
912 {
913         return params2string(inset_.params());
914 }
915
916
917 void InsetIncludeMailer::string2params(string const & in,
918                                        InsetCommandParams & params)
919 {
920         params.clear();
921         if (in.empty())
922                 return;
923
924         istringstream data(in);
925         Lexer lex(0,0);
926         lex.setStream(data);
927
928         string name;
929         lex >> name;
930         if (!lex || name != name_)
931                 return print_mailer_error("InsetIncludeMailer", in, 1, name_);
932
933         // This is part of the inset proper that is usually swallowed
934         // by Text::readInset
935         string id;
936         lex >> id;
937         if (!lex || id != "Include")
938                 return print_mailer_error("InsetIncludeMailer", in, 2, "Include");
939
940         InsetInclude inset(params);
941         inset.read(lex);
942         params = inset.params();
943 }
944
945
946 string const
947 InsetIncludeMailer::params2string(InsetCommandParams const & params)
948 {
949         InsetInclude inset(params);
950         ostringstream data;
951         data << name_ << ' ';
952         inset.write(data);
953         data << "\\end_inset\n";
954         return data.str();
955 }
956
957
958 } // namespace lyx