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 namespace lyx::support;
48 using std::istringstream;
50 using std::ostringstream;
53 extern BufferList bufferlist;
56 class InsetInclude::PreviewImpl : public lyx::graphics::PreviewedInset {
59 PreviewImpl(InsetInclude & p) : PreviewedInset(p) {}
62 bool previewWanted() const;
64 string const latexString() const;
66 InsetInclude & parent() const {
67 return *static_cast<InsetInclude*>(inset());
71 bool monitoring() const { return monitor_.get(); }
73 void startMonitoring();
75 void stopMonitoring() { monitor_.reset(); }
78 /// Invoked by monitor_ should the parent file change.
79 void restartLoading();
81 boost::scoped_ptr<FileMonitor> monitor_;
87 string const uniqueID()
89 static unsigned int seed = 1000;
90 return "file" + tostr(++seed);
96 InsetInclude::InsetInclude(Params const & p)
97 : params_(p), include_label(uniqueID()),
98 preview_(new PreviewImpl(*this)),
103 InsetInclude::InsetInclude(InsetCommandParams const & p, Buffer const & b)
104 : include_label(uniqueID()),
105 preview_(new PreviewImpl(*this)),
109 params_.masterFilename_ = b.fileName();
113 InsetInclude::InsetInclude(InsetInclude const & other)
115 params_(other.params_),
116 include_label(other.include_label),
117 preview_(new PreviewImpl(*this)),
118 set_label_(other.set_label_)
122 InsetInclude::~InsetInclude()
124 InsetIncludeMailer mailer(*this);
129 dispatch_result InsetInclude::localDispatch(FuncRequest const & cmd)
131 switch (cmd.action) {
133 case LFUN_INSET_MODIFY: {
134 InsetInclude::Params p;
135 InsetIncludeMailer::string2params(cmd.argument, p);
136 if (!p.cparams.getCmdName().empty()) {
138 params_.masterFilename_ = cmd.view()->buffer()->fileName();
139 cmd.view()->updateInset(this);
144 case LFUN_INSET_DIALOG_UPDATE:
145 InsetIncludeMailer(*this).updateDialog(cmd.view());
148 case LFUN_MOUSE_RELEASE:
149 if (button_.box().contains(cmd.x, cmd.y))
150 InsetIncludeMailer(*this).showDialog(cmd.view());
153 case LFUN_INSET_DIALOG_SHOW:
154 InsetIncludeMailer(*this).showDialog(cmd.view());
158 return InsetOld::localDispatch(cmd);
163 InsetInclude::Params const & InsetInclude::params() const
169 bool InsetInclude::Params::operator==(Params const & o) const
171 return cparams == o.cparams && flag == o.flag &&
172 masterFilename_ == o.masterFilename_;
176 bool InsetInclude::Params::operator!=(Params const & o) const
178 return !(*this == o);
182 void InsetInclude::set(Params const & p)
188 switch (params_.flag) {
193 command="verbatiminput";
199 command="verbatiminput*";
203 params_.cparams.setCmdName(command);
205 if (preview_->monitoring())
206 preview_->stopMonitoring();
208 if (lyx::graphics::PreviewedInset::activated() && params_.flag == INPUT)
209 preview_->generatePreview();
213 auto_ptr<InsetBase> InsetInclude::clone() const
216 //p.masterFilename_ = buffer.fileName();
217 #warning FIXME: broken cross-doc copy/paste - must fix
219 return auto_ptr<InsetBase>(new InsetInclude(params_));
223 void InsetInclude::write(Buffer const &, ostream & os) const
229 void InsetInclude::write(ostream & os) const
231 os << "Include " << params_.cparams.getCommand() << '\n'
232 << "preview " << tostr(params_.cparams.preview()) << '\n';
236 void InsetInclude::read(Buffer const &, LyXLex & lex)
242 void InsetInclude::read(LyXLex & lex)
244 params_.cparams.read(lex);
246 if (params_.cparams.getCmdName() == "include")
247 params_.flag = INCLUDE;
248 else if (params_.cparams.getCmdName() == "input")
249 params_.flag = INPUT;
250 /* FIXME: is this logic necessary now ? */
251 else if (contains(params_.cparams.getCmdName(), "verbatim")) {
253 if (params_.cparams.getCmdName() == "verbatiminput*")
254 params_.flag = VERBAST;
259 string const InsetInclude::getScreenLabel(Buffer const &) const
263 switch (params_.flag) {
264 case INPUT: temp += _("Input"); break;
265 case VERB: temp += _("Verbatim Input"); break;
266 case VERBAST: temp += _("Verbatim Input*"); break;
267 case INCLUDE: temp += _("Include"); break;
272 if (params_.cparams.getContents().empty())
275 temp += params_.cparams.getContents();
281 string const InsetInclude::getFileName() const
283 return MakeAbsPath(params_.cparams.getContents(),
284 OnlyPath(getMasterFilename()));
288 string const InsetInclude::getMasterFilename() const
290 return params_.masterFilename_;
294 bool InsetInclude::loadIfNeeded() const
299 if (!IsLyXFilename(getFileName()))
302 if (bufferlist.exists(getFileName()))
305 // the readonly flag can/will be wrong, not anymore I think.
306 FileInfo finfo(getFileName());
309 return loadLyXFile(bufferlist.newBuffer(getFileName()),
314 int InsetInclude::latex(Buffer const & buffer, ostream & os,
315 LatexRunParams const & runparams) const
317 string incfile(params_.cparams.getContents());
319 // Do nothing if no file name has been specified
323 if (loadIfNeeded()) {
324 Buffer * tmp = bufferlist.getBuffer(getFileName());
326 // FIXME: this should be a GUI warning
327 if (tmp->params().textclass != buffer.params().textclass) {
328 lyxerr << "WARNING: Included file `"
329 << MakeDisplayPath(getFileName())
330 << "' has textclass `"
331 << tmp->params().getLyXTextClass().name()
332 << "' while parent file has textclass `"
333 << buffer.params().getLyXTextClass().name()
338 // write it to a file (so far the complete file)
339 string writefile = ChangeExtension(getFileName(), ".tex");
341 if (!buffer.temppath().empty() && !runparams.nice) {
342 incfile = subst(incfile, '/','@');
344 incfile = subst(incfile, ':', '$');
346 writefile = AddName(buffer.temppath(), incfile);
348 writefile = getFileName();
349 writefile = ChangeExtension(writefile, ".tex");
350 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
351 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
353 tmp->markDepClean(buffer.temppath());
355 tmp->makeLaTeXFile(writefile, OnlyPath(getMasterFilename()),
360 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
361 } else if (params_.flag == INPUT) {
362 // \input wants file with extension (default is .tex)
363 if (!IsLyXFilename(getFileName())) {
364 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
366 os << '\\' << params_.cparams.getCmdName() << '{'
367 << ChangeExtension(incfile, ".tex")
371 // \include don't want extension and demands that the
372 // file really have .tex
373 os << '\\' << params_.cparams.getCmdName() << '{'
374 << ChangeExtension(incfile, string())
382 int InsetInclude::ascii(Buffer const &, ostream & os, int) const
385 os << GetFileContents(getFileName());
390 int InsetInclude::linuxdoc(Buffer const & buffer, ostream & os) const
392 string incfile(params_.cparams.getContents());
394 // Do nothing if no file name has been specified
398 if (loadIfNeeded()) {
399 Buffer * tmp = bufferlist.getBuffer(getFileName());
401 // write it to a file (so far the complete file)
402 string writefile = ChangeExtension(getFileName(), ".sgml");
403 if (!buffer.temppath().empty() && !buffer.niceFile()) {
404 incfile = subst(incfile, '/','@');
405 writefile = AddName(buffer.temppath(), incfile);
407 writefile = getFileName();
409 if (IsLyXFilename(getFileName()))
410 writefile = ChangeExtension(writefile, ".sgml");
412 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
413 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
415 tmp->makeLinuxDocFile(writefile, buffer.niceFile(), true);
420 << GetFileContents(getFileName())
423 os << '&' << include_label << ';';
429 int InsetInclude::docbook(Buffer const & buffer, ostream & os,
430 bool /*mixcont*/) const
432 string incfile(params_.cparams.getContents());
434 // Do nothing if no file name has been specified
438 if (loadIfNeeded()) {
439 Buffer * tmp = bufferlist.getBuffer(getFileName());
441 // write it to a file (so far the complete file)
442 string writefile = ChangeExtension(getFileName(), ".sgml");
443 if (!buffer.temppath().empty() && !buffer.niceFile()) {
444 incfile = subst(incfile, '/','@');
445 writefile = AddName(buffer.temppath(), incfile);
447 writefile = getFileName();
448 if (IsLyXFilename(getFileName()))
449 writefile = ChangeExtension(writefile, ".sgml");
451 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
452 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
454 tmp->makeDocBookFile(writefile, buffer.niceFile(), true);
458 os << "<inlinegraphic fileref=\""
459 << '&' << include_label << ';'
460 << "\" format=\"linespecific\">";
462 os << '&' << include_label << ';';
468 void InsetInclude::validate(LaTeXFeatures & features) const
471 string incfile(params_.cparams.getContents());
474 Buffer const & b = *bufferlist.getBuffer(getMasterFilename());
476 if (!b.temppath().empty() && !b.niceFile() && !isVerbatim()) {
477 incfile = subst(incfile, '/','@');
478 writefile = AddName(b.temppath(), incfile);
480 writefile = getFileName();
482 if (IsLyXFilename(getFileName()))
483 writefile = ChangeExtension(writefile, ".sgml");
485 features.includeFile(include_label, writefile);
488 features.require("verbatim");
490 // Here we must do the fun stuff...
491 // Load the file in the include if it needs
493 if (loadIfNeeded()) {
495 Buffer * const tmp = bufferlist.getBuffer(getFileName());
497 tmp->niceFile() = b.niceFile();
498 tmp->validate(features);
504 void InsetInclude::getLabelList(std::vector<string> & list) const
506 if (loadIfNeeded()) {
507 Buffer * tmp = bufferlist.getBuffer(getFileName());
508 tmp->setParentName("");
509 tmp->getLabelList(list);
510 tmp->setParentName(getMasterFilename());
515 void InsetInclude::fillWithBibKeys(std::vector<std::pair<string,string> > & keys) const
517 if (loadIfNeeded()) {
518 Buffer * tmp = bufferlist.getBuffer(getFileName());
519 tmp->setParentName("");
520 tmp->fillWithBibKeys(keys);
521 tmp->setParentName(getMasterFilename());
526 void InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
528 if (preview_->previewReady()) {
529 dim.asc = preview_->pimage()->ascent();
530 dim.des = preview_->pimage()->descent();
531 dim.wid = preview_->pimage()->width();
535 button_.update(getScreenLabel(*mi.base.bv->buffer()),
536 editable() != NOT_EDITABLE);
538 button_.metrics(mi, dim);
540 int center_indent = (params_.flag == INPUT ? 0 :
541 (mi.base.textwidth - dim.wid) / 2);
542 Box b(center_indent, center_indent + dim.wid, -dim.asc, dim.des);
545 dim.wid = mi.base.textwidth;
550 void InsetInclude::draw(PainterInfo & pi, int x, int y) const
553 if (!preview_->previewReady()) {
554 button_.draw(pi, x + button_.box().x1, y);
558 if (!preview_->monitoring())
559 preview_->startMonitoring();
561 pi.pain.image(x + button_.box().x1, y - dim_.asc, dim_.wid, dim_.height(),
562 *(preview_->pimage()->image()));
566 BufferView * InsetInclude::view() const
568 return button_.view();
576 void InsetInclude::addPreview(lyx::graphics::PreviewLoader & ploader) const
578 preview_->addPreview(ploader);
582 bool InsetInclude::PreviewImpl::previewWanted() const
584 return parent().params_.flag == InsetInclude::INPUT &&
585 parent().params_.cparams.preview() &&
586 IsFileReadable(parent().getFileName());
590 string const InsetInclude::PreviewImpl::latexString() const
592 if (!view() || !view()->buffer())
596 LatexRunParams runparams;
597 runparams.flavor = LatexRunParams::LATEX;
598 parent().latex(*view()->buffer(), os, runparams);
600 return STRCONV(os.str());
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()
614 lyxerr << "restartLoading()" << std::endl;
617 view()->updateInset(&parent());
622 string const InsetIncludeMailer::name_("include");
624 InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
629 string const InsetIncludeMailer::inset2string(Buffer const &) const
631 return params2string(inset_.params());
635 void InsetIncludeMailer::string2params(string const & in,
636 InsetInclude::Params & params)
638 params = InsetInclude::Params();
643 istringstream data(STRCONV(in));
649 string const token = lex.getString();
654 // This is part of the inset proper that is usually swallowed
655 // by Buffer::readInset
658 string const token = lex.getString();
659 if (token != "Include")
664 InsetInclude inset(params);
666 params = inset.params();
672 InsetIncludeMailer::params2string(InsetInclude::Params const & params)
674 InsetInclude inset(params);
677 data << name_ << ' ';
679 data << "\\end_inset\n";
680 return STRCONV(data.str());