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"
15 #include "bufferlist.h"
16 #include "BufferView.h"
18 #include "funcrequest.h"
20 #include "LaTeXFeatures.h"
21 #include "latexrunparams.h"
25 #include "metricsinfo.h"
26 #include "dimension.h"
28 #include "frontends/Dialogs.h"
29 #include "frontends/LyXView.h"
30 #include "frontends/Painter.h"
32 #include "support/filetools.h"
33 #include "support/FileInfo.h"
34 #include "support/FileMonitor.h"
35 #include "support/lstrings.h" // contains
36 #include "support/tostr.h"
38 #include "graphics/PreviewedInset.h"
39 #include "graphics/PreviewImage.h"
41 #include <boost/bind.hpp>
50 extern BufferList bufferlist;
53 class InsetInclude::PreviewImpl : public grfx::PreviewedInset {
56 PreviewImpl(InsetInclude & p) : PreviewedInset(p) {}
59 bool previewWanted() const;
61 string const latexString() const;
63 InsetInclude & parent() const {
64 return *static_cast<InsetInclude*>(inset());
68 bool monitoring() const { return monitor_.get(); }
70 void startMonitoring();
72 void stopMonitoring() { monitor_.reset(); }
75 /// Invoked by monitor_ should the parent file change.
76 void restartLoading();
78 boost::scoped_ptr<FileMonitor> monitor_;
84 string const uniqueID()
86 static unsigned int seed = 1000;
87 return "file" + tostr(++seed);
93 InsetInclude::InsetInclude(Params const & p)
94 : params_(p), include_label(uniqueID()),
95 preview_(new PreviewImpl(*this))
99 InsetInclude::InsetInclude(InsetCommandParams const & p, Buffer const & b)
100 : include_label(uniqueID()),
101 preview_(new PreviewImpl(*this))
104 params_.masterFilename_ = b.fileName();
108 InsetInclude::~InsetInclude()
110 InsetIncludeMailer mailer(*this);
115 dispatch_result InsetInclude::localDispatch(FuncRequest const & cmd)
117 switch (cmd.action) {
119 case LFUN_INSET_MODIFY: {
120 InsetInclude::Params p;
121 InsetIncludeMailer::string2params(cmd.argument, p);
122 if (!p.cparams.getCmdName().empty()) {
124 params_.masterFilename_ = cmd.view()->buffer()->fileName();
125 cmd.view()->updateInset(this);
130 case LFUN_INSET_DIALOG_UPDATE:
131 InsetIncludeMailer(*this).updateDialog(cmd.view());
134 case LFUN_MOUSE_RELEASE:
135 case LFUN_INSET_EDIT:
136 InsetIncludeMailer(*this).showDialog(cmd.view());
140 return InsetButton::localDispatch(cmd);
145 InsetInclude::Params const & InsetInclude::params() const
151 bool InsetInclude::Params::operator==(Params const & o) const
153 return cparams == o.cparams && flag == o.flag &&
154 masterFilename_ == o.masterFilename_;
158 bool InsetInclude::Params::operator!=(Params const & o) const
160 return !(*this == o);
164 void InsetInclude::set(Params const & p)
170 switch (params_.flag) {
175 command="verbatiminput";
181 command="verbatiminput*";
185 params_.cparams.setCmdName(command);
187 if (preview_->monitoring())
188 preview_->stopMonitoring();
190 if (grfx::PreviewedInset::activated() && params_.flag == INPUT)
191 preview_->generatePreview();
195 Inset * InsetInclude::clone(Buffer const & buffer) const
198 p.masterFilename_ = buffer.fileName();
200 return new InsetInclude(p);
204 // Inset * InsetInclude::clone(Buffer const & buffer, bool) const
206 // Params p(params_);
207 // p.masterFilename_ = buffer.fileName();
209 // return new InsetInclude(p);
213 void InsetInclude::write(Buffer const *, ostream & os) const
215 os << "Include " << params_.cparams.getCommand() << '\n'
216 << "preview " << tostr(params_.cparams.preview()) << '\n';
220 void InsetInclude::read(Buffer const *, LyXLex & lex)
222 params_.cparams.read(lex);
224 if (params_.cparams.getCmdName() == "include")
225 params_.flag = INCLUDE;
226 else if (params_.cparams.getCmdName() == "input")
227 params_.flag = INPUT;
228 /* FIXME: is this logic necessary now ? */
229 else if (contains(params_.cparams.getCmdName(), "verbatim")) {
231 if (params_.cparams.getCmdName() == "verbatiminput*")
232 params_.flag = VERBAST;
237 bool InsetInclude::display() const
239 return !(params_.flag == INPUT);
243 string const InsetInclude::getScreenLabel(Buffer const *) const
247 switch (params_.flag) {
248 case INPUT: temp += _("Input"); break;
249 case VERB: temp += _("Verbatim Input"); break;
250 case VERBAST: temp += _("Verbatim Input*"); break;
251 case INCLUDE: temp += _("Include"); break;
256 if (params_.cparams.getContents().empty())
259 temp += params_.cparams.getContents();
265 string const InsetInclude::getFileName() const
267 return MakeAbsPath(params_.cparams.getContents(),
268 OnlyPath(getMasterFilename()));
272 string const InsetInclude::getMasterFilename() const
274 return params_.masterFilename_;
278 bool InsetInclude::loadIfNeeded() const
283 if (!IsLyXFilename(getFileName()))
286 if (bufferlist.exists(getFileName()))
289 // the readonly flag can/will be wrong, not anymore I think.
290 FileInfo finfo(getFileName());
294 return bufferlist.loadLyXFile(getFileName(), false) != 0;
298 int InsetInclude::latex(Buffer const * buffer, ostream & os,
299 LatexRunParams const & runparams) const
301 string incfile(params_.cparams.getContents());
303 // Do nothing if no file name has been specified
307 if (loadIfNeeded()) {
308 Buffer * tmp = bufferlist.getBuffer(getFileName());
310 // FIXME: this should be a GUI warning
311 if (tmp->params.textclass != buffer->params.textclass) {
312 lyxerr << "WARNING: Included file `"
313 << MakeDisplayPath(getFileName())
314 << "' has textclass `"
315 << tmp->params.getLyXTextClass().name()
316 << "' while parent file has textclass `"
317 << buffer->params.getLyXTextClass().name()
322 // write it to a file (so far the complete file)
323 string writefile = ChangeExtension(getFileName(), ".tex");
325 if (!buffer->tmppath.empty() && !runparams.nice) {
326 incfile = subst(incfile, '/','@');
328 incfile = subst(incfile, ':', '$');
330 writefile = AddName(buffer->tmppath, incfile);
332 writefile = getFileName();
333 writefile = ChangeExtension(writefile, ".tex");
334 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
335 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
337 tmp->markDepClean(buffer->tmppath);
339 tmp->makeLaTeXFile(writefile, OnlyPath(getMasterFilename()),
344 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
345 } else if (params_.flag == INPUT) {
346 // \input wants file with extension (default is .tex)
347 if (!IsLyXFilename(getFileName())) {
348 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
350 os << '\\' << params_.cparams.getCmdName() << '{'
351 << ChangeExtension(incfile, ".tex")
355 // \include don't want extension and demands that the
356 // file really have .tex
357 os << '\\' << params_.cparams.getCmdName() << '{'
358 << ChangeExtension(incfile, string())
366 int InsetInclude::ascii(Buffer const *, ostream & os, int) const
369 os << GetFileContents(getFileName());
374 int InsetInclude::linuxdoc(Buffer const * buffer, ostream & os) const
376 string incfile(params_.cparams.getContents());
378 // Do nothing if no file name has been specified
382 if (loadIfNeeded()) {
383 Buffer * tmp = bufferlist.getBuffer(getFileName());
385 // write it to a file (so far the complete file)
386 string writefile = ChangeExtension(getFileName(), ".sgml");
387 if (!buffer->tmppath.empty() && !buffer->niceFile) {
388 incfile = subst(incfile, '/','@');
389 writefile = AddName(buffer->tmppath, incfile);
391 writefile = getFileName();
393 if (IsLyXFilename(getFileName()))
394 writefile = ChangeExtension(writefile, ".sgml");
396 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
397 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
399 tmp->makeLinuxDocFile(writefile, buffer->niceFile, true);
404 << GetFileContents(getFileName())
407 os << '&' << include_label << ';';
413 int InsetInclude::docbook(Buffer const * buffer, ostream & os,
414 bool /*mixcont*/) const
416 string incfile(params_.cparams.getContents());
418 // Do nothing if no file name has been specified
422 if (loadIfNeeded()) {
423 Buffer * tmp = bufferlist.getBuffer(getFileName());
425 // write it to a file (so far the complete file)
426 string writefile = ChangeExtension(getFileName(), ".sgml");
427 if (!buffer->tmppath.empty() && !buffer->niceFile) {
428 incfile = subst(incfile, '/','@');
429 writefile = AddName(buffer->tmppath, incfile);
431 writefile = getFileName();
432 if (IsLyXFilename(getFileName()))
433 writefile = ChangeExtension(writefile, ".sgml");
435 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
436 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
438 tmp->makeDocBookFile(writefile, buffer->niceFile, true);
442 os << "<inlinegraphic fileref=\""
443 << '&' << include_label << ';'
444 << "\" format=\"linespecific\">";
446 os << '&' << include_label << ';';
452 void InsetInclude::validate(LaTeXFeatures & features) const
455 string incfile(params_.cparams.getContents());
458 Buffer const * const b = bufferlist.getBuffer(getMasterFilename());
460 if (b && !b->tmppath.empty() && !b->niceFile && !isVerbatim()) {
461 incfile = subst(incfile, '/','@');
462 writefile = AddName(b->tmppath, incfile);
464 writefile = getFileName();
466 if (IsLyXFilename(getFileName()))
467 writefile = ChangeExtension(writefile, ".sgml");
469 features.includeFile(include_label, writefile);
472 features.require("verbatim");
474 // Here we must do the fun stuff...
475 // Load the file in the include if it needs
477 if (loadIfNeeded()) {
479 Buffer * const tmp = bufferlist.getBuffer(getFileName());
482 tmp->niceFile = b->niceFile;
483 tmp->validate(features);
489 vector<string> const InsetInclude::getLabelList() const
493 if (loadIfNeeded()) {
494 Buffer * tmp = bufferlist.getBuffer(getFileName());
495 tmp->setParentName("");
496 l = tmp->getLabelList();
497 tmp->setParentName(getMasterFilename());
504 void InsetInclude::fillWithBibKeys(vector<pair<string,string> > & keys) const
506 if (loadIfNeeded()) {
507 Buffer * tmp = bufferlist.getBuffer(getFileName());
508 tmp->setParentName("");
509 tmp->fillWithBibKeys(keys);
510 tmp->setParentName(getMasterFilename());
515 int InsetInclude::ascent(BufferView * bv, LyXFont const & font) const
517 return preview_->previewReady() ?
518 preview_->pimage()->ascent() : InsetButton::ascent(bv, font);
522 int InsetInclude::descent(BufferView * bv, LyXFont const & font) const
524 return preview_->previewReady() ?
525 preview_->pimage()->descent() : InsetButton::descent(bv, font);
529 int InsetInclude::width(BufferView * bv, LyXFont const & font) const
531 return preview_->previewReady() ?
532 preview_->pimage()->width() : InsetButton::width(bv, font);
536 void InsetInclude::draw(PainterInfo & pi, int x, int y) const
539 if (!preview_->previewReady()) {
540 InsetButton::draw(pi, x, y);
544 if (!preview_->monitoring())
545 preview_->startMonitoring();
548 dimension(pi.base.bv, pi.base.font, dim);
550 pi.pain.image(x, y - dim.asc, dim.wid, dim.height(),
551 *(preview_->pimage()->image()));
559 void InsetInclude::addPreview(grfx::PreviewLoader & ploader) const
561 preview_->addPreview(ploader);
565 bool InsetInclude::PreviewImpl::previewWanted() const
567 return parent().params_.flag == InsetInclude::INPUT &&
568 parent().params_.cparams.preview() &&
569 IsFileReadable(parent().getFileName());
573 string const InsetInclude::PreviewImpl::latexString() const
575 if (!view() || !view()->buffer())
579 LatexRunParams runparams;
580 runparams.flavor = LatexRunParams::LATEX;
581 parent().latex(view()->buffer(), os, runparams);
583 return STRCONV(os.str());
587 void InsetInclude::PreviewImpl::startMonitoring()
589 monitor_.reset(new FileMonitor(parent().getFileName(), 2000));
590 monitor_->connect(boost::bind(&PreviewImpl::restartLoading, this));
595 void InsetInclude::PreviewImpl::restartLoading()
597 lyxerr << "restartLoading()" << std::endl;
600 view()->updateInset(&parent());
605 string const InsetIncludeMailer::name_("include");
607 InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
612 string const InsetIncludeMailer::inset2string() const
614 return params2string(inset_.params());
618 void InsetIncludeMailer::string2params(string const & in,
619 InsetInclude::Params & params)
621 params = InsetInclude::Params();
626 istringstream data(STRCONV(in));
632 string const token = lex.getString();
637 // This is part of the inset proper that is usually swallowed
638 // by Buffer::readInset
641 string const token = lex.getString();
642 if (token != "Include")
647 InsetInclude inset(params);
649 params = inset.params();
655 InsetIncludeMailer::params2string(InsetInclude::Params const & params)
657 InsetInclude inset(params);
660 data << name_ << ' ';
661 inset.write(0, data);
662 data << "\\end_inset\n";
663 return STRCONV(data.str());