3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
8 * Full author contact details are available in file CREDITS.
13 #include "insetinclude.h"
16 #include "buffer_funcs.h"
17 #include "bufferlist.h"
18 #include "bufferparams.h"
19 #include "BufferView.h"
21 #include "funcrequest.h"
23 #include "LaTeXFeatures.h"
24 #include "latexrunparams.h"
26 #include "metricsinfo.h"
28 #include "frontends/Painter.h"
30 #include "graphics/PreviewedInset.h"
31 #include "graphics/PreviewImage.h"
33 #include "support/FileInfo.h"
34 #include "support/FileMonitor.h"
35 #include "support/filetools.h"
36 #include "support/lstrings.h" // contains
37 #include "support/tostr.h"
39 #include <boost/bind.hpp>
41 #include "support/std_sstream.h"
43 using lyx::support::AddName;
44 using lyx::support::ChangeExtension;
45 using lyx::support::contains;
46 using lyx::support::FileInfo;
47 using lyx::support::FileMonitor;
48 using lyx::support::GetFileContents;
49 using lyx::support::IsFileReadable;
50 using lyx::support::IsLyXFilename;
51 using lyx::support::MakeAbsPath;
52 using lyx::support::MakeDisplayPath;
53 using lyx::support::OnlyFilename;
54 using lyx::support::OnlyPath;
55 using lyx::support::subst;
60 using std::istringstream;
62 using std::ostringstream;
65 extern BufferList bufferlist;
68 class InsetInclude::PreviewImpl : public lyx::graphics::PreviewedInset {
71 PreviewImpl(InsetInclude & p) : PreviewedInset(p) {}
74 bool previewWanted(Buffer const &) const;
76 string const latexString(Buffer const &) const;
78 InsetInclude const & parent() const {
79 return dynamic_cast<InsetInclude const &>(inset());
83 bool monitoring() const { return monitor_.get(); }
85 void startMonitoring(string const & file);
87 void stopMonitoring() { monitor_.reset(); }
90 /// Invoked by monitor_ should the parent file change.
91 void restartLoading();
93 boost::scoped_ptr<FileMonitor> monitor_;
99 string const uniqueID()
101 static unsigned int seed = 1000;
102 return "file" + tostr(++seed);
108 InsetInclude::InsetInclude(InsetCommandParams const & p)
109 : params_(p), include_label(uniqueID()),
110 preview_(new PreviewImpl(*this)),
115 InsetInclude::InsetInclude(InsetInclude const & other)
117 params_(other.params_),
118 include_label(other.include_label),
119 preview_(new PreviewImpl(*this)),
120 set_label_(other.set_label_)
124 InsetInclude::~InsetInclude()
126 InsetIncludeMailer mailer(*this);
131 dispatch_result InsetInclude::localDispatch(FuncRequest const & cmd)
133 switch (cmd.action) {
135 case LFUN_INSET_MODIFY: {
136 InsetCommandParams p;
137 InsetIncludeMailer::string2params(cmd.argument, p);
138 if (!p.getCmdName().empty()) {
139 set(p, *cmd.view()->buffer());
140 cmd.view()->updateInset(this);
145 case LFUN_INSET_DIALOG_UPDATE:
146 InsetIncludeMailer(*this).updateDialog(cmd.view());
149 case LFUN_MOUSE_RELEASE:
150 if (button_.box().contains(cmd.x, cmd.y))
151 InsetIncludeMailer(*this).showDialog(cmd.view());
154 case LFUN_INSET_DIALOG_SHOW:
155 InsetIncludeMailer(*this).showDialog(cmd.view());
159 return InsetOld::localDispatch(cmd);
164 InsetCommandParams const & InsetInclude::params() const
172 /// the type of inclusion
181 Types type(InsetCommandParams const & params)
183 string const command_name = params.getCmdName();
185 if (command_name == "input")
187 if (command_name == "verbatiminput")
189 if (command_name == "verbatiminput*")
195 bool isVerbatim(InsetCommandParams const & params)
197 string const command_name = params.getCmdName();
198 return command_name == "verbatiminput" ||
199 command_name == "verbatiminput*";
205 void InsetInclude::set(InsetCommandParams const & p, Buffer const & buffer)
210 if (preview_->monitoring())
211 preview_->stopMonitoring();
213 if (lyx::graphics::PreviewedInset::activated() &&
214 type(params_) == INPUT)
215 preview_->generatePreview(buffer);
219 auto_ptr<InsetBase> InsetInclude::clone() const
221 return auto_ptr<InsetBase>(new InsetInclude(*this));
225 void InsetInclude::write(Buffer const &, ostream & os) const
231 void InsetInclude::write(ostream & os) const
233 os << "Include " << params_.getCommand() << '\n'
234 << "preview " << tostr(params_.preview()) << '\n';
238 void InsetInclude::read(Buffer const &, LyXLex & lex)
244 void InsetInclude::read(LyXLex & lex)
250 string const InsetInclude::getScreenLabel(Buffer const &) const
254 switch (type(params_)) {
255 case INPUT: temp += _("Input"); break;
256 case VERB: temp += _("Verbatim Input"); break;
257 case VERBAST: temp += _("Verbatim Input*"); break;
258 case INCLUDE: temp += _("Include"); break;
263 if (params_.getContents().empty())
266 temp += OnlyFilename(params_.getContents());
274 string const masterFilename(Buffer const & buffer)
276 return buffer.fileName();
280 string const includedFilename(Buffer const & buffer,
281 InsetCommandParams const & params)
283 return MakeAbsPath(params.getContents(),
284 OnlyPath(masterFilename(buffer)));
288 /// return true if the file is or got loaded.
289 bool loadIfNeeded(Buffer const & buffer, InsetCommandParams const & params)
291 if (isVerbatim(params))
294 string const included_file = includedFilename(buffer, params);
295 if (!IsLyXFilename(included_file))
298 if (bufferlist.exists(included_file))
301 // the readonly flag can/will be wrong, not anymore I think.
302 FileInfo finfo(included_file);
305 return loadLyXFile(bufferlist.newBuffer(included_file),
313 int InsetInclude::latex(Buffer const & buffer, ostream & os,
314 LatexRunParams const & runparams) const
316 string incfile(params_.getContents());
318 // Do nothing if no file name has been specified
322 string const included_file = includedFilename(buffer, params_);
324 if (loadIfNeeded(buffer, params_)) {
325 Buffer * tmp = bufferlist.getBuffer(included_file);
327 // FIXME: this should be a GUI warning
328 if (tmp->params().textclass != buffer.params().textclass) {
329 lyxerr << "WARNING: Included file `"
330 << MakeDisplayPath(included_file)
331 << "' has textclass `"
332 << tmp->params().getLyXTextClass().name()
333 << "' while parent file has textclass `"
334 << buffer.params().getLyXTextClass().name()
339 // write it to a file (so far the complete file)
340 string writefile = ChangeExtension(included_file, ".tex");
342 if (!buffer.temppath().empty() && !runparams.nice) {
343 incfile = subst(incfile, '/','@');
345 incfile = subst(incfile, ':', '$');
347 writefile = AddName(buffer.temppath(), incfile);
349 writefile = included_file;
350 writefile = ChangeExtension(writefile, ".tex");
351 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
352 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
354 tmp->markDepClean(buffer.temppath());
356 tmp->makeLaTeXFile(writefile,
357 OnlyPath(masterFilename(buffer)),
361 if (isVerbatim(params_)) {
362 os << '\\' << params_.getCmdName() << '{' << incfile << '}';
363 } else if (type(params_) == INPUT) {
364 // \input wants file with extension (default is .tex)
365 if (!IsLyXFilename(included_file)) {
366 os << '\\' << params_.getCmdName() << '{' << incfile << '}';
368 os << '\\' << params_.getCmdName() << '{'
369 << ChangeExtension(incfile, ".tex")
373 // \include don't want extension and demands that the
374 // file really have .tex
375 os << '\\' << params_.getCmdName() << '{'
376 << ChangeExtension(incfile, string())
384 int InsetInclude::ascii(Buffer const & buffer, ostream & os, int) const
386 if (isVerbatim(params_))
387 os << GetFileContents(includedFilename(buffer, params_));
392 int InsetInclude::linuxdoc(Buffer const & buffer, ostream & os) const
394 string incfile(params_.getContents());
396 // Do nothing if no file name has been specified
400 string const included_file = includedFilename(buffer, params_);
402 if (loadIfNeeded(buffer, params_)) {
403 Buffer * tmp = bufferlist.getBuffer(included_file);
405 // write it to a file (so far the complete file)
406 string writefile = ChangeExtension(included_file, ".sgml");
407 if (!buffer.temppath().empty() && !buffer.niceFile()) {
408 incfile = subst(incfile, '/','@');
409 writefile = AddName(buffer.temppath(), incfile);
411 writefile = included_file;
413 if (IsLyXFilename(included_file))
414 writefile = ChangeExtension(writefile, ".sgml");
416 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
417 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
419 tmp->makeLinuxDocFile(writefile, buffer.niceFile(), true);
422 if (isVerbatim(params_)) {
424 << GetFileContents(included_file)
427 os << '&' << include_label << ';';
433 int InsetInclude::docbook(Buffer const & buffer, ostream & os,
434 bool /*mixcont*/) const
436 string incfile(params_.getContents());
438 // Do nothing if no file name has been specified
442 string const included_file = includedFilename(buffer, params_);
444 if (loadIfNeeded(buffer, params_)) {
445 Buffer * tmp = bufferlist.getBuffer(included_file);
447 // write it to a file (so far the complete file)
448 string writefile = ChangeExtension(included_file, ".sgml");
449 if (!buffer.temppath().empty() && !buffer.niceFile()) {
450 incfile = subst(incfile, '/','@');
451 writefile = AddName(buffer.temppath(), incfile);
453 writefile = included_file;
454 if (IsLyXFilename(included_file))
455 writefile = ChangeExtension(writefile, ".sgml");
457 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
458 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
460 tmp->makeDocBookFile(writefile, buffer.niceFile(), true);
463 if (isVerbatim(params_)) {
464 os << "<inlinegraphic fileref=\""
465 << '&' << include_label << ';'
466 << "\" format=\"linespecific\">";
468 os << '&' << include_label << ';';
474 void InsetInclude::validate(LaTeXFeatures & features) const
476 string incfile(params_.getContents());
479 Buffer const & buffer = features.buffer();
481 string const included_file = includedFilename(buffer, params_);
483 if (!buffer.temppath().empty() &&
484 !buffer.niceFile() &&
485 !isVerbatim(params_)) {
486 incfile = subst(incfile, '/','@');
487 writefile = AddName(buffer.temppath(), incfile);
489 writefile = included_file;
491 if (IsLyXFilename(included_file))
492 writefile = ChangeExtension(writefile, ".sgml");
494 features.includeFile(include_label, writefile);
496 if (isVerbatim(params_))
497 features.require("verbatim");
499 // Here we must do the fun stuff...
500 // Load the file in the include if it needs
502 if (loadIfNeeded(buffer, params_)) {
504 Buffer * const tmp = bufferlist.getBuffer(included_file);
506 tmp->niceFile() = buffer.niceFile();
507 tmp->validate(features);
513 void InsetInclude::getLabelList(Buffer const & buffer,
514 std::vector<string> & list) const
516 if (loadIfNeeded(buffer, params_)) {
517 string const included_file = includedFilename(buffer, params_);
518 Buffer * tmp = bufferlist.getBuffer(included_file);
519 tmp->setParentName("");
520 tmp->getLabelList(list);
521 tmp->setParentName(masterFilename(buffer));
526 void InsetInclude::fillWithBibKeys(Buffer const & buffer,
527 std::vector<std::pair<string,string> > & keys) const
529 if (loadIfNeeded(buffer, params_)) {
530 string const included_file = includedFilename(buffer, params_);
531 Buffer * tmp = bufferlist.getBuffer(included_file);
532 tmp->setParentName("");
533 tmp->fillWithBibKeys(keys);
534 tmp->setParentName(masterFilename(buffer));
539 void InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
541 if (preview_->previewReady()) {
542 dim.asc = preview_->pimage()->ascent();
543 dim.des = preview_->pimage()->descent();
544 dim.wid = preview_->pimage()->width();
548 button_.update(getScreenLabel(*mi.base.bv->buffer()),
549 editable() != NOT_EDITABLE);
551 button_.metrics(mi, dim);
553 int center_indent = type(params_) == INPUT ?
554 0 : (mi.base.textwidth - dim.wid) / 2;
555 Box b(center_indent, center_indent + dim.wid, -dim.asc, dim.des);
558 dim.wid = mi.base.textwidth;
563 void InsetInclude::draw(PainterInfo & pi, int x, int y) const
566 if (!preview_->previewReady()) {
567 button_.draw(pi, x + button_.box().x1, y);
571 if (!preview_->monitoring()) {
572 string const included_file =
573 includedFilename(*view()->buffer(), params_);
574 preview_->startMonitoring(included_file);
577 pi.pain.image(x + button_.box().x1, y - dim_.asc, dim_.wid, dim_.height(),
578 *(preview_->pimage()->image()));
582 BufferView * InsetInclude::view() const
584 return button_.view();
592 void InsetInclude::addPreview(lyx::graphics::PreviewLoader & ploader) const
594 preview_->addPreview(ploader);
598 bool InsetInclude::PreviewImpl::previewWanted(Buffer const & buffer) const
600 string const included_file = includedFilename(buffer, parent().params());
602 return type(parent().params_) == INPUT &&
603 parent().params_.preview() &&
604 IsFileReadable(included_file);
608 string const InsetInclude::PreviewImpl::latexString(Buffer const & buffer) const
611 LatexRunParams runparams;
612 runparams.flavor = LatexRunParams::LATEX;
613 parent().latex(buffer, os, runparams);
619 void InsetInclude::PreviewImpl::startMonitoring(string const & file)
621 monitor_.reset(new FileMonitor(file, 2000));
622 monitor_->connect(boost::bind(&PreviewImpl::restartLoading, this));
627 void InsetInclude::PreviewImpl::restartLoading()
632 view()->updateInset(&parent());
633 if (view()->buffer())
634 generatePreview(*view()->buffer());
638 string const InsetIncludeMailer::name_("include");
640 InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
645 string const InsetIncludeMailer::inset2string(Buffer const &) const
647 return params2string(inset_.params());
651 void InsetIncludeMailer::string2params(string const & in,
652 InsetCommandParams & params)
654 params = InsetCommandParams();
659 istringstream data(in);
665 string const token = lex.getString();
670 // This is part of the inset proper that is usually swallowed
671 // by Buffer::readInset
674 string const token = lex.getString();
675 if (token != "Include")
680 InsetInclude inset(params);
682 params = inset.params();
688 InsetIncludeMailer::params2string(InsetCommandParams const & params)
690 InsetInclude inset(params);
692 data << name_ << ' ';
694 data << "\\end_inset\n";