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"
26 #include "frontends/Dialogs.h"
27 #include "frontends/LyXView.h"
28 #include "frontends/Painter.h"
30 #include "support/filetools.h"
31 #include "support/FileInfo.h"
32 #include "support/FileMonitor.h"
33 #include "support/lstrings.h" // contains
34 #include "support/tostr.h"
36 #include "graphics/PreviewedInset.h"
37 #include "graphics/PreviewImage.h"
39 #include <boost/bind.hpp>
48 extern BufferList bufferlist;
51 class InsetInclude::PreviewImpl : public grfx::PreviewedInset {
54 PreviewImpl(InsetInclude & p) : PreviewedInset(p) {}
57 bool previewWanted() const;
59 string const latexString() const;
61 InsetInclude & parent() const {
62 return *static_cast<InsetInclude*>(inset());
66 bool monitoring() const { return monitor_.get(); }
68 void startMonitoring();
70 void stopMonitoring() { monitor_.reset(); }
73 /// Invoked by monitor_ should the parent file change.
74 void restartLoading();
76 boost::scoped_ptr<FileMonitor> monitor_;
82 string const uniqueID()
84 static unsigned int seed = 1000;
85 return "file" + tostr(++seed);
91 InsetInclude::InsetInclude(Params const & p)
92 : params_(p), include_label(uniqueID()),
93 preview_(new PreviewImpl(*this))
97 InsetInclude::InsetInclude(InsetCommandParams const & p, Buffer const & b)
98 : include_label(uniqueID()),
99 preview_(new PreviewImpl(*this))
102 params_.masterFilename_ = b.fileName();
106 InsetInclude::~InsetInclude()
108 InsetIncludeMailer mailer(*this);
113 dispatch_result InsetInclude::localDispatch(FuncRequest const & cmd)
115 dispatch_result result = UNDISPATCHED;
117 switch (cmd.action) {
118 case LFUN_INSET_MODIFY: {
119 InsetInclude::Params p;
120 InsetIncludeMailer::string2params(cmd.argument, p);
121 if (p.cparams.getCmdName().empty())
125 params_.masterFilename_ = cmd.view()->buffer()->fileName();
127 cmd.view()->updateInset(this);
132 case LFUN_INSET_DIALOG_UPDATE: {
133 InsetIncludeMailer mailer(*this);
134 mailer.updateDialog(cmd.view());
138 case LFUN_MOUSE_RELEASE:
139 edit(cmd.view(), cmd.x, cmd.y, cmd.button());
150 InsetInclude::Params const & InsetInclude::params() const
156 bool InsetInclude::Params::operator==(Params const & o) const
158 if (cparams == o.cparams && flag == o.flag &&
159 masterFilename_ == o.masterFilename_)
166 bool InsetInclude::Params::operator!=(Params const & o) const
168 return !(*this == o);
172 void InsetInclude::set(Params const & p)
178 switch (params_.flag) {
183 command="verbatiminput";
189 command="verbatiminput*";
193 params_.cparams.setCmdName(command);
195 if (preview_->monitoring())
196 preview_->stopMonitoring();
198 if (grfx::PreviewedInset::activated() && params_.flag == INPUT)
199 preview_->generatePreview();
203 Inset * InsetInclude::clone(Buffer const & buffer, bool) const
206 p.masterFilename_ = buffer.fileName();
208 return new InsetInclude(p);
212 void InsetInclude::edit(BufferView * bv, int, int, mouse_button::state)
214 InsetIncludeMailer mailer(*this);
215 mailer.showDialog(bv);
219 void InsetInclude::edit(BufferView * bv, bool)
221 edit(bv, 0, 0, mouse_button::none);
225 void InsetInclude::write(Buffer const *, ostream & os) const
227 os << "Include " << params_.cparams.getCommand() << '\n'
228 << "preview " << tostr(params_.cparams.preview()) << '\n';
232 void InsetInclude::read(Buffer const *, LyXLex & lex)
234 params_.cparams.read(lex);
236 if (params_.cparams.getCmdName() == "include")
237 params_.flag = INCLUDE;
238 else if (params_.cparams.getCmdName() == "input")
239 params_.flag = INPUT;
240 /* FIXME: is this logic necessary now ? */
241 else if (contains(params_.cparams.getCmdName(), "verbatim")) {
243 if (params_.cparams.getCmdName() == "verbatiminput*")
244 params_.flag = VERBAST;
249 bool InsetInclude::display() const
251 return !(params_.flag == INPUT);
255 string const InsetInclude::getScreenLabel(Buffer const *) const
259 switch (params_.flag) {
260 case INPUT: temp += _("Input"); break;
261 case VERB: temp += _("Verbatim Input"); break;
262 case VERBAST: temp += _("Verbatim Input*"); break;
263 case INCLUDE: temp += _("Include"); break;
268 if (params_.cparams.getContents().empty())
271 temp += params_.cparams.getContents();
277 string const InsetInclude::getFileName() const
279 return MakeAbsPath(params_.cparams.getContents(),
280 OnlyPath(getMasterFilename()));
284 string const InsetInclude::getMasterFilename() const
286 return params_.masterFilename_;
290 bool InsetInclude::loadIfNeeded() const
295 if (!IsLyXFilename(getFileName()))
298 if (bufferlist.exists(getFileName()))
301 // the readonly flag can/will be wrong, not anymore I think.
302 FileInfo finfo(getFileName());
306 return bufferlist.loadLyXFile(getFileName(), false) != 0;
310 int InsetInclude::latex(Buffer const * buffer, ostream & os,
311 bool /*fragile*/, bool /*fs*/) const
313 string incfile(params_.cparams.getContents());
315 // Do nothing if no file name has been specified
319 if (loadIfNeeded()) {
320 Buffer * tmp = bufferlist.getBuffer(getFileName());
322 // FIXME: this should be a GUI warning
323 if (tmp->params.textclass != buffer->params.textclass) {
324 lyxerr << "WARNING: Included file `"
325 << MakeDisplayPath(getFileName())
326 << "' has textclass `"
327 << tmp->params.getLyXTextClass().name()
328 << "' while parent file has textclass `"
329 << buffer->params.getLyXTextClass().name()
334 // write it to a file (so far the complete file)
335 string writefile = ChangeExtension(getFileName(), ".tex");
337 if (!buffer->tmppath.empty()
338 && !buffer->niceFile) {
339 incfile = subst(incfile, '/','@');
341 incfile = subst(incfile, ':', '$');
343 writefile = AddName(buffer->tmppath, incfile);
345 writefile = getFileName();
346 writefile = ChangeExtension(writefile, ".tex");
347 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
348 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
350 tmp->markDepClean(buffer->tmppath);
352 tmp->makeLaTeXFile(writefile,
353 OnlyPath(getMasterFilename()),
354 buffer->niceFile, true);
358 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
359 } else if (params_.flag == INPUT) {
360 // \input wants file with extension (default is .tex)
361 if (!IsLyXFilename(getFileName())) {
362 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
364 os << '\\' << params_.cparams.getCmdName() << '{'
365 << ChangeExtension(incfile, ".tex")
369 // \include don't want extension and demands that the
370 // file really have .tex
371 os << '\\' << params_.cparams.getCmdName() << '{'
372 << ChangeExtension(incfile, string())
380 int InsetInclude::ascii(Buffer const *, ostream & os, int) const
383 os << GetFileContents(getFileName());
388 int InsetInclude::linuxdoc(Buffer const * buffer, ostream & os) const
390 string incfile(params_.cparams.getContents());
392 // Do nothing if no file name has been specified
396 if (loadIfNeeded()) {
397 Buffer * tmp = bufferlist.getBuffer(getFileName());
399 // write it to a file (so far the complete file)
400 string writefile = ChangeExtension(getFileName(), ".sgml");
401 if (!buffer->tmppath.empty() && !buffer->niceFile) {
402 incfile = subst(incfile, '/','@');
403 writefile = AddName(buffer->tmppath, incfile);
405 writefile = getFileName();
407 if (IsLyXFilename(getFileName()))
408 writefile = ChangeExtension(writefile, ".sgml");
410 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
411 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
413 tmp->makeLinuxDocFile(writefile, buffer->niceFile, true);
418 << GetFileContents(getFileName())
421 os << '&' << include_label << ';';
427 int InsetInclude::docbook(Buffer const * buffer, ostream & os,
428 bool /*mixcont*/) const
430 string incfile(params_.cparams.getContents());
432 // Do nothing if no file name has been specified
436 if (loadIfNeeded()) {
437 Buffer * tmp = bufferlist.getBuffer(getFileName());
439 // write it to a file (so far the complete file)
440 string writefile = ChangeExtension(getFileName(), ".sgml");
441 if (!buffer->tmppath.empty() && !buffer->niceFile) {
442 incfile = subst(incfile, '/','@');
443 writefile = AddName(buffer->tmppath, incfile);
445 writefile = getFileName();
446 if (IsLyXFilename(getFileName()))
447 writefile = ChangeExtension(writefile, ".sgml");
449 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
450 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
452 tmp->makeDocBookFile(writefile, buffer->niceFile, true);
456 os << "<inlinegraphic fileref=\""
457 << '&' << include_label << ';'
458 << "\" format=\"linespecific\">";
460 os << '&' << include_label << ';';
466 void InsetInclude::validate(LaTeXFeatures & features) const
469 string incfile(params_.cparams.getContents());
472 Buffer const * const b = bufferlist.getBuffer(getMasterFilename());
474 if (b && !b->tmppath.empty() && !b->niceFile && !isVerbatim()) {
475 incfile = subst(incfile, '/','@');
476 writefile = AddName(b->tmppath, incfile);
478 writefile = getFileName();
480 if (IsLyXFilename(getFileName()))
481 writefile = ChangeExtension(writefile, ".sgml");
483 features.includeFile(include_label, writefile);
486 features.require("verbatim");
488 // Here we must do the fun stuff...
489 // Load the file in the include if it needs
491 if (loadIfNeeded()) {
493 Buffer * const tmp = bufferlist.getBuffer(getFileName());
496 tmp->niceFile = b->niceFile;
497 tmp->validate(features);
503 vector<string> const InsetInclude::getLabelList() const
507 if (loadIfNeeded()) {
508 Buffer * tmp = bufferlist.getBuffer(getFileName());
509 tmp->setParentName("");
510 l = tmp->getLabelList();
511 tmp->setParentName(getMasterFilename());
518 void InsetInclude::fillWithBibKeys(vector<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 int InsetInclude::ascent(BufferView * bv, LyXFont const & font) const
531 return preview_->previewReady() ?
532 preview_->pimage()->ascent() : InsetButton::ascent(bv, font);
536 int InsetInclude::descent(BufferView * bv, LyXFont const & font) const
538 return preview_->previewReady() ?
539 preview_->pimage()->descent() : InsetButton::descent(bv, font);
543 int InsetInclude::width(BufferView * bv, LyXFont const & font) const
545 return preview_->previewReady() ?
546 preview_->pimage()->width() : InsetButton::width(bv, font);
550 void InsetInclude::draw(BufferView * bv, LyXFont const & font, int y,
554 if (!preview_->previewReady()) {
555 InsetButton::draw(bv, font, y, xx);
559 if (!preview_->monitoring())
560 preview_->startMonitoring();
562 int const x = int(xx);
563 int const w = width(bv, font);
564 int const d = descent(bv, font);
565 int const a = ascent(bv, font);
568 bv->painter().image(x, y - a, w, h,
569 *(preview_->pimage()->image()));
579 void InsetInclude::addPreview(grfx::PreviewLoader & ploader) const
581 preview_->addPreview(ploader);
585 bool InsetInclude::PreviewImpl::previewWanted() const
587 return parent().params_.flag == InsetInclude::INPUT &&
588 parent().params_.cparams.preview() &&
589 IsFileReadable(parent().getFileName());
593 string const InsetInclude::PreviewImpl::latexString() const
595 if (!view() || !view()->buffer())
599 parent().latex(view()->buffer(), os, false, false);
601 return STRCONV(os.str());
605 void InsetInclude::PreviewImpl::startMonitoring()
607 monitor_.reset(new FileMonitor(parent().getFileName(), 2000));
608 monitor_->connect(boost::bind(&PreviewImpl::restartLoading, this));
613 void InsetInclude::PreviewImpl::restartLoading()
615 lyxerr << "restartLoading()" << std::endl;
618 view()->updateInset(&parent());
623 string const InsetIncludeMailer::name_("include");
625 InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
630 string const InsetIncludeMailer::inset2string() const
632 return params2string(inset_.params());
636 void InsetIncludeMailer::string2params(string const & in,
637 InsetInclude::Params & params)
639 params = InsetInclude::Params();
644 istringstream data(STRCONV(in));
650 string const token = lex.getString();
655 // This is part of the inset proper that is usually swallowed
656 // by Buffer::readInset
659 string const token = lex.getString();
660 if (token != "Include")
665 InsetInclude inset(params);
667 params = inset.params();
673 InsetIncludeMailer::params2string(InsetInclude::Params const & params)
675 InsetInclude inset(params);
678 data << name_ << ' ';
679 inset.write(0, data);
680 data << "\\end_inset\n";
681 return STRCONV(data.str());