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::OnlyPath;
54 using lyx::support::subst;
59 using std::istringstream;
61 using std::ostringstream;
64 extern BufferList bufferlist;
67 class InsetInclude::PreviewImpl : public lyx::graphics::PreviewedInset {
70 PreviewImpl(InsetInclude & p) : PreviewedInset(p) {}
73 bool previewWanted() const;
75 string const latexString(Buffer const &) const;
77 InsetInclude const & parent() const {
78 return dynamic_cast<InsetInclude const &>(inset());
82 bool monitoring() const { return monitor_.get(); }
84 void startMonitoring();
86 void stopMonitoring() { monitor_.reset(); }
89 /// Invoked by monitor_ should the parent file change.
90 void restartLoading();
92 boost::scoped_ptr<FileMonitor> monitor_;
98 string const uniqueID()
100 static unsigned int seed = 1000;
101 return "file" + tostr(++seed);
107 InsetInclude::InsetInclude(Params const & p)
108 : params_(p), include_label(uniqueID()),
109 preview_(new PreviewImpl(*this)),
114 InsetInclude::InsetInclude(InsetCommandParams const & p, Buffer const & b)
115 : include_label(uniqueID()),
116 preview_(new PreviewImpl(*this)),
120 params_.masterFilename_ = b.fileName();
124 InsetInclude::InsetInclude(InsetInclude const & other)
126 params_(other.params_),
127 include_label(other.include_label),
128 preview_(new PreviewImpl(*this)),
129 set_label_(other.set_label_)
133 InsetInclude::~InsetInclude()
135 InsetIncludeMailer mailer(*this);
140 dispatch_result InsetInclude::localDispatch(FuncRequest const & cmd)
142 switch (cmd.action) {
144 case LFUN_INSET_MODIFY: {
145 InsetInclude::Params p;
146 InsetIncludeMailer::string2params(cmd.argument, p);
147 if (!p.cparams.getCmdName().empty()) {
148 p.masterFilename_ = cmd.view()->buffer()->fileName();
150 cmd.view()->updateInset(this);
155 case LFUN_INSET_DIALOG_UPDATE:
156 InsetIncludeMailer(*this).updateDialog(cmd.view());
159 case LFUN_MOUSE_RELEASE:
160 if (button_.box().contains(cmd.x, cmd.y))
161 InsetIncludeMailer(*this).showDialog(cmd.view());
164 case LFUN_INSET_DIALOG_SHOW:
165 InsetIncludeMailer(*this).showDialog(cmd.view());
169 return InsetOld::localDispatch(cmd);
174 InsetInclude::Params const & InsetInclude::params() const
182 /// the type of inclusion
191 Types type(InsetInclude::Params const & params)
193 string const command_name = params.cparams.getCmdName();
195 if (command_name == "input")
197 if (command_name == "verbatiminput")
199 if (command_name == "verbatiminput*")
205 bool isVerbatim(InsetInclude::Params const & params)
207 string const command_name = params.cparams.getCmdName();
208 return command_name == "verbatiminput" ||
209 command_name == "verbatiminput*";
215 void InsetInclude::set(Params const & p)
219 if (preview_->monitoring())
220 preview_->stopMonitoring();
222 if (lyx::graphics::PreviewedInset::activated() &&
223 type(params_) == INPUT)
224 preview_->generatePreview();
228 auto_ptr<InsetBase> InsetInclude::clone() const
231 //p.masterFilename_ = buffer.fileName();
232 #warning FIXME: broken cross-doc copy/paste - must fix
234 return auto_ptr<InsetBase>(new InsetInclude(params_));
238 void InsetInclude::write(Buffer const &, ostream & os) const
244 void InsetInclude::write(ostream & os) const
246 os << "Include " << params_.cparams.getCommand() << '\n'
247 << "preview " << tostr(params_.cparams.preview()) << '\n';
251 void InsetInclude::read(Buffer const &, LyXLex & lex)
257 void InsetInclude::read(LyXLex & lex)
259 params_.cparams.read(lex);
263 string const InsetInclude::getScreenLabel(Buffer const &) const
267 switch (type(params_)) {
268 case INPUT: temp += _("Input"); break;
269 case VERB: temp += _("Verbatim Input"); break;
270 case VERBAST: temp += _("Verbatim Input*"); break;
271 case INCLUDE: temp += _("Include"); break;
276 if (params_.cparams.getContents().empty())
279 temp += params_.cparams.getContents();
285 string const InsetInclude::getFileName() const
287 return MakeAbsPath(params_.cparams.getContents(),
288 OnlyPath(getMasterFilename()));
292 string const InsetInclude::getMasterFilename() const
294 return params_.masterFilename_;
298 bool InsetInclude::loadIfNeeded() const
300 if (isVerbatim(params_))
303 if (!IsLyXFilename(getFileName()))
306 if (bufferlist.exists(getFileName()))
309 // the readonly flag can/will be wrong, not anymore I think.
310 FileInfo finfo(getFileName());
313 return loadLyXFile(bufferlist.newBuffer(getFileName()),
318 int InsetInclude::latex(Buffer const & buffer, ostream & os,
319 LatexRunParams const & runparams) const
321 string incfile(params_.cparams.getContents());
323 // Do nothing if no file name has been specified
327 if (loadIfNeeded()) {
328 Buffer * tmp = bufferlist.getBuffer(getFileName());
330 // FIXME: this should be a GUI warning
331 if (tmp->params().textclass != buffer.params().textclass) {
332 lyxerr << "WARNING: Included file `"
333 << MakeDisplayPath(getFileName())
334 << "' has textclass `"
335 << tmp->params().getLyXTextClass().name()
336 << "' while parent file has textclass `"
337 << buffer.params().getLyXTextClass().name()
342 // write it to a file (so far the complete file)
343 string writefile = ChangeExtension(getFileName(), ".tex");
345 if (!buffer.temppath().empty() && !runparams.nice) {
346 incfile = subst(incfile, '/','@');
348 incfile = subst(incfile, ':', '$');
350 writefile = AddName(buffer.temppath(), incfile);
352 writefile = getFileName();
353 writefile = ChangeExtension(writefile, ".tex");
354 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
355 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
357 tmp->markDepClean(buffer.temppath());
359 tmp->makeLaTeXFile(writefile, OnlyPath(getMasterFilename()),
363 if (isVerbatim(params_)) {
364 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
365 } else if (type(params_) == INPUT) {
366 // \input wants file with extension (default is .tex)
367 if (!IsLyXFilename(getFileName())) {
368 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
370 os << '\\' << params_.cparams.getCmdName() << '{'
371 << ChangeExtension(incfile, ".tex")
375 // \include don't want extension and demands that the
376 // file really have .tex
377 os << '\\' << params_.cparams.getCmdName() << '{'
378 << ChangeExtension(incfile, string())
386 int InsetInclude::ascii(Buffer const &, ostream & os, int) const
388 if (isVerbatim(params_))
389 os << GetFileContents(getFileName());
394 int InsetInclude::linuxdoc(Buffer const & buffer, ostream & os) const
396 string incfile(params_.cparams.getContents());
398 // Do nothing if no file name has been specified
402 if (loadIfNeeded()) {
403 Buffer * tmp = bufferlist.getBuffer(getFileName());
405 // write it to a file (so far the complete file)
406 string writefile = ChangeExtension(getFileName(), ".sgml");
407 if (!buffer.temppath().empty() && !buffer.niceFile()) {
408 incfile = subst(incfile, '/','@');
409 writefile = AddName(buffer.temppath(), incfile);
411 writefile = getFileName();
413 if (IsLyXFilename(getFileName()))
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(getFileName())
427 os << '&' << include_label << ';';
433 int InsetInclude::docbook(Buffer const & buffer, ostream & os,
434 bool /*mixcont*/) const
436 string incfile(params_.cparams.getContents());
438 // Do nothing if no file name has been specified
442 if (loadIfNeeded()) {
443 Buffer * tmp = bufferlist.getBuffer(getFileName());
445 // write it to a file (so far the complete file)
446 string writefile = ChangeExtension(getFileName(), ".sgml");
447 if (!buffer.temppath().empty() && !buffer.niceFile()) {
448 incfile = subst(incfile, '/','@');
449 writefile = AddName(buffer.temppath(), incfile);
451 writefile = getFileName();
452 if (IsLyXFilename(getFileName()))
453 writefile = ChangeExtension(writefile, ".sgml");
455 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
456 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
458 tmp->makeDocBookFile(writefile, buffer.niceFile(), true);
461 if (isVerbatim(params_)) {
462 os << "<inlinegraphic fileref=\""
463 << '&' << include_label << ';'
464 << "\" format=\"linespecific\">";
466 os << '&' << include_label << ';';
472 void InsetInclude::validate(LaTeXFeatures & features) const
474 string incfile(params_.cparams.getContents());
477 Buffer const & b = features.buffer();
479 if (!b.temppath().empty() && !b.niceFile() && !isVerbatim(params_)) {
480 incfile = subst(incfile, '/','@');
481 writefile = AddName(b.temppath(), incfile);
483 writefile = getFileName();
485 if (IsLyXFilename(getFileName()))
486 writefile = ChangeExtension(writefile, ".sgml");
488 features.includeFile(include_label, writefile);
490 if (isVerbatim(params_))
491 features.require("verbatim");
493 // Here we must do the fun stuff...
494 // Load the file in the include if it needs
496 if (loadIfNeeded()) {
498 Buffer * const tmp = bufferlist.getBuffer(getFileName());
500 tmp->niceFile() = b.niceFile();
501 tmp->validate(features);
507 void InsetInclude::getLabelList(std::vector<string> & list) const
509 if (loadIfNeeded()) {
510 Buffer * tmp = bufferlist.getBuffer(getFileName());
511 tmp->setParentName("");
512 tmp->getLabelList(list);
513 tmp->setParentName(getMasterFilename());
518 void InsetInclude::fillWithBibKeys(std::vector<std::pair<string,string> > & keys) const
520 if (loadIfNeeded()) {
521 Buffer * tmp = bufferlist.getBuffer(getFileName());
522 tmp->setParentName("");
523 tmp->fillWithBibKeys(keys);
524 tmp->setParentName(getMasterFilename());
529 void InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
531 if (preview_->previewReady()) {
532 dim.asc = preview_->pimage()->ascent();
533 dim.des = preview_->pimage()->descent();
534 dim.wid = preview_->pimage()->width();
538 button_.update(getScreenLabel(*mi.base.bv->buffer()),
539 editable() != NOT_EDITABLE);
541 button_.metrics(mi, dim);
543 int center_indent = type(params_) == INPUT ?
544 0 : (mi.base.textwidth - dim.wid) / 2;
545 Box b(center_indent, center_indent + dim.wid, -dim.asc, dim.des);
548 dim.wid = mi.base.textwidth;
553 void InsetInclude::draw(PainterInfo & pi, int x, int y) const
556 if (!preview_->previewReady()) {
557 button_.draw(pi, x + button_.box().x1, y);
561 if (!preview_->monitoring())
562 preview_->startMonitoring();
564 pi.pain.image(x + button_.box().x1, y - dim_.asc, dim_.wid, dim_.height(),
565 *(preview_->pimage()->image()));
569 BufferView * InsetInclude::view() const
571 return button_.view();
579 void InsetInclude::addPreview(lyx::graphics::PreviewLoader & ploader) const
581 preview_->addPreview(ploader);
585 bool InsetInclude::PreviewImpl::previewWanted() const
587 return type(parent().params_) == INPUT &&
588 parent().params_.cparams.preview() &&
589 IsFileReadable(parent().getFileName());
593 string const InsetInclude::PreviewImpl::latexString(Buffer const & buffer) const
596 LatexRunParams runparams;
597 runparams.flavor = LatexRunParams::LATEX;
598 parent().latex(buffer, os, runparams);
604 void InsetInclude::PreviewImpl::startMonitoring()
606 monitor_.reset(new FileMonitor(parent().getFileName(), 2000));
607 monitor_->connect(boost::bind(&PreviewImpl::restartLoading, this));
612 void InsetInclude::PreviewImpl::restartLoading()
616 view()->updateInset(&parent());
621 string const InsetIncludeMailer::name_("include");
623 InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
628 string const InsetIncludeMailer::inset2string(Buffer const &) const
630 return params2string(inset_.params());
634 void InsetIncludeMailer::string2params(string const & in,
635 InsetInclude::Params & params)
637 params = InsetInclude::Params();
642 istringstream data(in);
648 string const token = lex.getString();
653 // This is part of the inset proper that is usually swallowed
654 // by Buffer::readInset
657 string const token = lex.getString();
658 if (token != "Include")
663 InsetInclude inset(params);
665 params = inset.params();
671 InsetIncludeMailer::params2string(InsetInclude::Params const & params)
673 InsetInclude inset(params);
676 data << name_ << ' ';
678 data << "\\end_inset\n";