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 PreviewedInset {
71 PreviewImpl(InsetInclude const & p) : parent_(p) {}
74 bool previewWanted(Buffer const &) const;
76 string const latexString(Buffer const &) const;
79 bool monitoring() const { return monitor_.get(); }
81 void startMonitoring(string const & file);
83 void stopMonitoring() { monitor_.reset(); }
86 /// Invoked by monitor_ should the parent file change.
87 void restartLoading();
89 boost::scoped_ptr<FileMonitor> monitor_;
91 InsetInclude const & parent_;
97 string const uniqueID()
99 static unsigned int seed = 1000;
100 return "file" + tostr(++seed);
106 InsetInclude::InsetInclude(InsetCommandParams const & p)
107 : params_(p), include_label(uniqueID()),
108 preview_(new PreviewImpl(*this)),
111 preview_->connect(boost::bind(&InsetInclude::statusChanged, 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_)
122 preview_->connect(boost::bind(&InsetInclude::statusChanged, this));
126 InsetInclude::~InsetInclude()
128 InsetIncludeMailer mailer(*this);
133 dispatch_result InsetInclude::localDispatch(FuncRequest const & cmd)
135 switch (cmd.action) {
137 case LFUN_INSET_MODIFY: {
138 InsetCommandParams p;
139 InsetIncludeMailer::string2params(cmd.argument, p);
140 if (!p.getCmdName().empty()) {
141 set(p, *cmd.view()->buffer());
142 cmd.view()->updateInset(this);
147 case LFUN_INSET_DIALOG_UPDATE:
148 InsetIncludeMailer(*this).updateDialog(cmd.view());
151 case LFUN_MOUSE_RELEASE:
152 if (button_.box().contains(cmd.x, cmd.y))
153 InsetIncludeMailer(*this).showDialog(cmd.view());
156 case LFUN_INSET_DIALOG_SHOW:
157 InsetIncludeMailer(*this).showDialog(cmd.view());
161 return InsetOld::localDispatch(cmd);
166 InsetCommandParams const & InsetInclude::params() const
174 /// the type of inclusion
183 Types type(InsetCommandParams const & params)
185 string const command_name = params.getCmdName();
187 if (command_name == "input")
189 if (command_name == "verbatiminput")
191 if (command_name == "verbatiminput*")
197 bool isVerbatim(InsetCommandParams const & params)
199 string const command_name = params.getCmdName();
200 return command_name == "verbatiminput" ||
201 command_name == "verbatiminput*";
207 void InsetInclude::set(InsetCommandParams const & p, Buffer const & buffer)
212 if (preview_->monitoring())
213 preview_->stopMonitoring();
215 if (PreviewedInset::activated() && type(params_) == INPUT)
216 preview_->generatePreview(buffer);
220 auto_ptr<InsetBase> InsetInclude::clone() const
222 return auto_ptr<InsetBase>(new InsetInclude(*this));
226 void InsetInclude::write(Buffer const &, ostream & os) const
232 void InsetInclude::write(ostream & os) const
234 os << "Include " << params_.getCommand() << '\n'
235 << "preview " << tostr(params_.preview()) << '\n';
239 void InsetInclude::read(Buffer const &, LyXLex & lex)
245 void InsetInclude::read(LyXLex & lex)
251 string const InsetInclude::getScreenLabel(Buffer const &) const
255 switch (type(params_)) {
256 case INPUT: temp += _("Input"); break;
257 case VERB: temp += _("Verbatim Input"); break;
258 case VERBAST: temp += _("Verbatim Input*"); break;
259 case INCLUDE: temp += _("Include"); break;
264 if (params_.getContents().empty())
267 temp += OnlyFilename(params_.getContents());
275 string const masterFilename(Buffer const & buffer)
277 return buffer.fileName();
281 string const includedFilename(Buffer const & buffer,
282 InsetCommandParams const & params)
284 return MakeAbsPath(params.getContents(),
285 OnlyPath(masterFilename(buffer)));
289 /// return true if the file is or got loaded.
290 bool loadIfNeeded(Buffer const & buffer, InsetCommandParams const & params)
292 if (isVerbatim(params))
295 string const included_file = includedFilename(buffer, params);
296 if (!IsLyXFilename(included_file))
299 if (bufferlist.exists(included_file))
302 // the readonly flag can/will be wrong, not anymore I think.
303 FileInfo finfo(included_file);
306 return loadLyXFile(bufferlist.newBuffer(included_file),
314 int InsetInclude::latex(Buffer const & buffer, ostream & os,
315 LatexRunParams const & runparams) const
317 string incfile(params_.getContents());
319 // Do nothing if no file name has been specified
323 string const included_file = includedFilename(buffer, params_);
325 if (loadIfNeeded(buffer, params_)) {
326 Buffer * tmp = bufferlist.getBuffer(included_file);
328 // FIXME: this should be a GUI warning
329 if (tmp->params().textclass != buffer.params().textclass) {
330 lyxerr << "WARNING: Included file `"
331 << MakeDisplayPath(included_file)
332 << "' has textclass `"
333 << tmp->params().getLyXTextClass().name()
334 << "' while parent file has textclass `"
335 << buffer.params().getLyXTextClass().name()
340 // write it to a file (so far the complete file)
341 string writefile = ChangeExtension(included_file, ".tex");
343 if (!buffer.temppath().empty() && !runparams.nice) {
344 incfile = subst(incfile, '/','@');
346 incfile = subst(incfile, ':', '$');
348 writefile = AddName(buffer.temppath(), incfile);
350 writefile = included_file;
351 writefile = ChangeExtension(writefile, ".tex");
352 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
353 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
355 tmp->markDepClean(buffer.temppath());
357 tmp->makeLaTeXFile(writefile,
358 OnlyPath(masterFilename(buffer)),
362 if (isVerbatim(params_)) {
363 os << '\\' << params_.getCmdName() << '{' << incfile << '}';
364 } else if (type(params_) == INPUT) {
365 // \input wants file with extension (default is .tex)
366 if (!IsLyXFilename(included_file)) {
367 os << '\\' << params_.getCmdName() << '{' << incfile << '}';
369 os << '\\' << params_.getCmdName() << '{'
370 << ChangeExtension(incfile, ".tex")
374 // \include don't want extension and demands that the
375 // file really have .tex
376 os << '\\' << params_.getCmdName() << '{'
377 << ChangeExtension(incfile, string())
385 int InsetInclude::ascii(Buffer const & buffer, ostream & os, int) const
387 if (isVerbatim(params_))
388 os << GetFileContents(includedFilename(buffer, params_));
393 int InsetInclude::linuxdoc(Buffer const & buffer, ostream & os) const
395 string incfile(params_.getContents());
397 // Do nothing if no file name has been specified
401 string const included_file = includedFilename(buffer, params_);
403 if (loadIfNeeded(buffer, params_)) {
404 Buffer * tmp = bufferlist.getBuffer(included_file);
406 // write it to a file (so far the complete file)
407 string writefile = ChangeExtension(included_file, ".sgml");
408 if (!buffer.temppath().empty() && !buffer.niceFile()) {
409 incfile = subst(incfile, '/','@');
410 writefile = AddName(buffer.temppath(), incfile);
412 writefile = included_file;
414 if (IsLyXFilename(included_file))
415 writefile = ChangeExtension(writefile, ".sgml");
417 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
418 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
420 tmp->makeLinuxDocFile(writefile, buffer.niceFile(), true);
423 if (isVerbatim(params_)) {
425 << GetFileContents(included_file)
428 os << '&' << include_label << ';';
434 int InsetInclude::docbook(Buffer const & buffer, ostream & os,
435 bool /*mixcont*/) const
437 string incfile(params_.getContents());
439 // Do nothing if no file name has been specified
443 string const included_file = includedFilename(buffer, params_);
445 if (loadIfNeeded(buffer, params_)) {
446 Buffer * tmp = bufferlist.getBuffer(included_file);
448 // write it to a file (so far the complete file)
449 string writefile = ChangeExtension(included_file, ".sgml");
450 if (!buffer.temppath().empty() && !buffer.niceFile()) {
451 incfile = subst(incfile, '/','@');
452 writefile = AddName(buffer.temppath(), incfile);
454 writefile = included_file;
455 if (IsLyXFilename(included_file))
456 writefile = ChangeExtension(writefile, ".sgml");
458 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
459 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
461 tmp->makeDocBookFile(writefile, buffer.niceFile(), true);
464 if (isVerbatim(params_)) {
465 os << "<inlinegraphic fileref=\""
466 << '&' << include_label << ';'
467 << "\" format=\"linespecific\">";
469 os << '&' << include_label << ';';
475 void InsetInclude::validate(LaTeXFeatures & features) const
477 string incfile(params_.getContents());
480 Buffer const & buffer = features.buffer();
482 string const included_file = includedFilename(buffer, params_);
484 if (!buffer.temppath().empty() &&
485 !buffer.niceFile() &&
486 !isVerbatim(params_)) {
487 incfile = subst(incfile, '/','@');
488 writefile = AddName(buffer.temppath(), incfile);
490 writefile = included_file;
492 if (IsLyXFilename(included_file))
493 writefile = ChangeExtension(writefile, ".sgml");
495 features.includeFile(include_label, writefile);
497 if (isVerbatim(params_))
498 features.require("verbatim");
500 // Here we must do the fun stuff...
501 // Load the file in the include if it needs
503 if (loadIfNeeded(buffer, params_)) {
505 Buffer * const tmp = bufferlist.getBuffer(included_file);
507 tmp->niceFile() = buffer.niceFile();
508 tmp->validate(features);
514 void InsetInclude::getLabelList(Buffer const & buffer,
515 std::vector<string> & list) const
517 if (loadIfNeeded(buffer, params_)) {
518 string const included_file = includedFilename(buffer, params_);
519 Buffer * tmp = bufferlist.getBuffer(included_file);
520 tmp->setParentName("");
521 tmp->getLabelList(list);
522 tmp->setParentName(masterFilename(buffer));
527 void InsetInclude::fillWithBibKeys(Buffer const & buffer,
528 std::vector<std::pair<string,string> > & keys) const
530 if (loadIfNeeded(buffer, params_)) {
531 string const included_file = includedFilename(buffer, params_);
532 Buffer * tmp = bufferlist.getBuffer(included_file);
533 tmp->setParentName("");
534 tmp->fillWithBibKeys(keys);
535 tmp->setParentName(masterFilename(buffer));
540 void InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
542 Buffer const * buffer_ptr = mi.base.bv ? mi.base.bv->buffer() : 0;
543 if (buffer_ptr && preview_->previewReady(*buffer_ptr)) {
544 dim.asc = preview_->pimage()->ascent();
545 dim.des = preview_->pimage()->descent();
546 dim.wid = preview_->pimage()->width();
550 button_.update(getScreenLabel(*mi.base.bv->buffer()),
551 editable() != NOT_EDITABLE);
553 button_.metrics(mi, dim);
555 int center_indent = type(params_) == INPUT ?
556 0 : (mi.base.textwidth - dim.wid) / 2;
557 Box b(center_indent, center_indent + dim.wid, -dim.asc, dim.des);
560 dim.wid = mi.base.textwidth;
565 void InsetInclude::draw(PainterInfo & pi, int x, int y) const
568 Buffer const * buffer_ptr = pi.base.bv ? pi.base.bv->buffer() : 0;
569 bool const use_preview = buffer_ptr && preview_->previewReady(*buffer_ptr);
572 button_.draw(pi, x + button_.box().x1, y);
576 if (!preview_->monitoring()) {
577 string const included_file =
578 includedFilename(*view()->buffer(), params_);
579 preview_->startMonitoring(included_file);
582 pi.pain.image(x + button_.box().x1, y - dim_.asc, dim_.wid, dim_.height(),
583 *(preview_->pimage()->image()));
587 BufferView * InsetInclude::view() const
589 return button_.view();
597 void InsetInclude::statusChanged() const
600 view()->updateInset(this);
604 void InsetInclude::addPreview(lyx::graphics::PreviewLoader & ploader) const
606 preview_->addPreview(ploader);
610 bool InsetInclude::PreviewImpl::previewWanted(Buffer const & buffer) const
612 string const included_file = includedFilename(buffer, parent_.params());
614 return type(parent_.params_) == INPUT &&
615 parent_.params_.preview() &&
616 IsFileReadable(included_file);
620 string const InsetInclude::PreviewImpl::latexString(Buffer const & buffer) const
623 LatexRunParams runparams;
624 runparams.flavor = LatexRunParams::LATEX;
625 parent_.latex(buffer, os, runparams);
631 void InsetInclude::PreviewImpl::startMonitoring(string const & file)
633 monitor_.reset(new FileMonitor(file, 2000));
634 monitor_->connect(boost::bind(&PreviewImpl::restartLoading, this));
639 void InsetInclude::PreviewImpl::restartLoading()
641 BufferView * const view = parent_.view();
644 view->updateInset(&parent_);
645 if (view->buffer()) {
646 Buffer const & buffer = *view->buffer();
647 removePreview(buffer);
648 generatePreview(buffer);
653 string const InsetIncludeMailer::name_("include");
655 InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
660 string const InsetIncludeMailer::inset2string(Buffer const &) const
662 return params2string(inset_.params());
666 void InsetIncludeMailer::string2params(string const & in,
667 InsetCommandParams & params)
669 params = InsetCommandParams();
674 istringstream data(in);
680 string const token = lex.getString();
685 // This is part of the inset proper that is usually swallowed
686 // by Buffer::readInset
689 string const token = lex.getString();
690 if (token != "Include")
695 InsetInclude inset(params);
697 params = inset.params();
703 InsetIncludeMailer::params2string(InsetCommandParams const & params)
705 InsetInclude inset(params);
707 data << name_ << ' ';
709 data << "\\end_inset\n";