2 * \file insetexternal.C
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup Nielsen
8 * Full author contact details are available in file CREDITS
13 #include "insetexternal.h"
14 #include "insets/renderers.h"
17 #include "BufferView.h"
18 #include "converter.h"
20 #include "ExternalTemplate.h"
21 #include "funcrequest.h"
23 #include "LaTeXFeatures.h"
24 #include "latexrunparams.h"
30 #include "frontends/lyx_gui.h"
31 #include "frontends/LyXView.h"
32 #include "frontends/Dialogs.h"
34 #include "support/FileInfo.h"
35 #include "support/filetools.h"
36 #include "support/forkedcall.h"
37 #include "support/lstrings.h"
38 #include "support/lyxalgo.h"
39 #include "support/path.h"
40 #include "support/tostr.h"
42 #include <boost/bind.hpp>
47 using namespace lyx::support;
55 lyx::graphics::DisplayType const defaultDisplayType = lyx::graphics::NoDisplay;
57 unsigned int defaultLyxScale = 100;
59 /// Substitute meta-variables in string s, makeing use of params and buffer.
60 string const doSubstitution(InsetExternal::Params const & params,
61 Buffer const * buffer, string const & s);
63 /// Invoke the external editor.
64 void editExternal(InsetExternal::Params const & params, Buffer const * buffer);
69 InsetExternal::Params::Params()
70 : display(defaultDisplayType),
71 lyxscale(defaultLyxScale)
73 tempname = tempName(string(), "lyxext");
75 // must have an extension for the converter code to work correctly.
80 InsetExternal::Params::~Params()
86 InsetExternal::InsetExternal()
87 : renderer_(new ButtonRenderer)
91 InsetExternal::InsetExternal(InsetExternal const & other)
93 boost::signals::trackable(),
94 params_(other.params_),
95 renderer_(other.renderer_->clone())
97 GraphicRenderer * ptr = dynamic_cast<GraphicRenderer *>(renderer_.get());
99 ptr->connect(boost::bind(&InsetExternal::statusChanged, this));
104 auto_ptr<InsetBase> InsetExternal::clone() const
106 return auto_ptr<InsetBase>(new InsetExternal(*this));
110 InsetExternal::~InsetExternal()
112 InsetExternalMailer(*this).hideDialog();
116 void InsetExternal::statusChanged()
118 BufferView * bv = renderer_->view();
120 bv->updateInset(this);
124 dispatch_result InsetExternal::localDispatch(FuncRequest const & cmd)
126 switch (cmd.action) {
128 case LFUN_EXTERNAL_EDIT: {
131 Buffer const & buffer = *cmd.view()->buffer();
132 InsetExternal::Params p;
133 InsetExternalMailer::string2params(cmd.argument, buffer, p);
134 editExternal(p, &buffer);
135 return DISPATCHED_NOUPDATE;
138 case LFUN_INSET_MODIFY: {
141 Buffer const * buffer = cmd.view()->buffer();
142 InsetExternal::Params p;
143 InsetExternalMailer::string2params(cmd.argument, *buffer, p);
144 setParams(p, buffer);
145 cmd.view()->updateInset(this);
149 case LFUN_INSET_DIALOG_UPDATE:
150 InsetExternalMailer(*this).updateDialog(cmd.view());
153 case LFUN_MOUSE_RELEASE:
154 case LFUN_INSET_EDIT:
155 InsetExternalMailer(*this).showDialog(cmd.view());
164 void InsetExternal::metrics(MetricsInfo & mi, Dimension & dim) const
166 renderer_->metrics(mi, dim);
171 void InsetExternal::draw(PainterInfo & pi, int x, int y) const
173 renderer_->draw(pi, x, y);
179 lyx::graphics::Params get_grfx_params(InsetExternal::Params const & eparams)
181 lyx::graphics::Params gparams;
183 gparams.filename = eparams.filename.absFilename();
184 gparams.scale = eparams.lyxscale;
185 gparams.display = eparams.display;
187 if (gparams.display == lyx::graphics::DefaultDisplay)
188 gparams.display = lyxrc.display_graphics;
190 // Override the above if we're not using a gui
191 if (!lyx_gui::use_gui)
192 gparams.display = lyx::graphics::NoDisplay;
198 ExternalTemplate const * getTemplatePtr(InsetExternal::Params const & params)
200 ExternalTemplateManager & etm = ExternalTemplateManager::get();
201 ExternalTemplate const & templ = etm.getTemplateByName(params.templatename);
202 if (templ.lyxName.empty())
208 string const getScreenLabel(InsetExternal::Params const & params,
209 Buffer const * buffer)
211 ExternalTemplate const * const ptr = getTemplatePtr(params);
213 return bformat(_("External template %1$s is not installed"),
214 params.templatename);
215 return doSubstitution(params, buffer, ptr->guiName);
221 InsetExternal::Params const & InsetExternal::params() const
227 void InsetExternal::setParams(Params const & p, Buffer const * buffer)
229 // The stored params; what we would like to happen in an ideal world.
230 params_.filename = p.filename;
231 params_.templatename = p.templatename;
232 params_.display = p.display;
233 params_.lyxscale = p.lyxscale;
235 // We display the inset as a button by default.
236 bool display_button = (!getTemplatePtr(params_) ||
237 params_.filename.empty() ||
238 params_.display == lyx::graphics::NoDisplay);
240 if (display_button) {
241 ButtonRenderer * button_ptr =
242 dynamic_cast<ButtonRenderer *>(renderer_.get());
244 button_ptr = new ButtonRenderer;
245 renderer_.reset(button_ptr);
248 button_ptr->update(getScreenLabel(params_, buffer), true);
251 GraphicRenderer * graphic_ptr =
252 dynamic_cast<GraphicRenderer *>(renderer_.get());
254 graphic_ptr = new GraphicRenderer;
255 graphic_ptr->connect(
256 boost::bind(&InsetExternal::statusChanged, this));
257 renderer_.reset(graphic_ptr);
260 graphic_ptr->update(get_grfx_params(params_));
265 void InsetExternal::write(Buffer const * buffer, ostream & os) const
268 << "\ttemplate " << params_.templatename << '\n';
270 if (!params_.filename.empty())
272 << params_.filename.outputFilename(buffer->filePath())
275 if (params_.display != defaultDisplayType)
276 os << "\tdisplay " << lyx::graphics::displayTranslator.find(params_.display)
279 if (params_.lyxscale != defaultLyxScale)
280 os << "\tlyxscale " << tostr(params_.lyxscale) << '\n';
284 void InsetExternal::read(Buffer const * buffer, LyXLex & lex)
294 keyword_item external_tags[] = {
295 { "\\end_inset", EX_END },
296 { "display", EX_DISPLAY},
297 { "filename", EX_FILENAME},
298 { "lyxscale", EX_LYXSCALE},
299 { "template", EX_TEMPLATE }
302 lex.pushTable(external_tags, EX_END);
304 bool found_end = false;
305 bool read_error = false;
307 InsetExternal::Params params;
312 params.templatename = lex.getString();
318 string const name = lex.getString();
319 params.filename.set(name, buffer->filePath());
325 string const name = lex.getString();
326 params.display = lyx::graphics::displayTranslator.find(name);
332 params.lyxscale = lex.getInteger();
341 lex.printError("ExternalInset::read: "
342 "Wrong tag: $$Token");
347 if (found_end || read_error)
352 lex.printError("ExternalInset::read: "
353 "Missing \\end_inset.");
358 // Replace the inset's store
359 setParams(params, buffer);
361 lyxerr[Debug::INFO] << "InsetExternal::Read: "
362 << "template: '" << params_.templatename
363 << "' filename: '" << params_.filename.absFilename()
364 << "' display: '" << params_.display
365 << "' scale: '" << params_.lyxscale
370 int InsetExternal::write(string const & format,
371 Buffer const * buf, ostream & os,
372 bool external_in_tmpdir) const
374 ExternalTemplate const * const et_ptr = getTemplatePtr(params_);
377 ExternalTemplate const & et = *et_ptr;
379 ExternalTemplate::Formats::const_iterator cit =
380 et.formats.find(format);
381 if (cit == et.formats.end()) {
382 lyxerr << "External template format '" << format
383 << "' not specified in template "
384 << params_.templatename << endl;
388 updateExternal(format, buf, external_in_tmpdir);
389 string const str = doSubstitution(params_, buf, cit->second.product);
391 return int(lyx::count(str.begin(), str.end(),'\n') + 1);
395 int InsetExternal::latex(Buffer const * buf, ostream & os,
396 LatexRunParams const & runparams) const
398 // "nice" means that the buffer is exported to LaTeX format but not
399 // run through the LaTeX compiler.
400 // If we're running through the LaTeX compiler, we should write the
401 // generated files in the bufer's temporary directory.
402 bool const external_in_tmpdir =
403 lyxrc.use_tempdir && !buf->tmppath.empty() && !runparams.nice;
405 // If the template has specified a PDFLaTeX output, then we try and
407 if (runparams.flavor == LatexRunParams::PDFLATEX) {
408 ExternalTemplate const * const et_ptr = getTemplatePtr(params_);
411 ExternalTemplate const & et = *et_ptr;
413 ExternalTemplate::Formats::const_iterator cit =
414 et.formats.find("PDFLaTeX");
415 if (cit != et.formats.end())
416 return write("PDFLaTeX", buf, os, external_in_tmpdir);
419 return write("LaTeX", buf, os, external_in_tmpdir);
423 int InsetExternal::ascii(Buffer const * buf, ostream & os, int) const
425 return write("Ascii", buf, os);
429 int InsetExternal::linuxdoc(Buffer const * buf, ostream & os) const
431 return write("LinuxDoc", buf, os);
435 int InsetExternal::docbook(Buffer const * buf, ostream & os, bool) const
437 return write("DocBook", buf, os);
441 void InsetExternal::validate(LaTeXFeatures & features) const
443 ExternalTemplate const * const et_ptr = getTemplatePtr(params_);
446 ExternalTemplate const & et = *et_ptr;
448 ExternalTemplate::Formats::const_iterator cit =
449 et.formats.find("LaTeX");
451 if (cit == et.formats.end())
454 if (!cit->second.requirement.empty()) {
455 features.require(cit->second.requirement);
457 if (!cit->second.preamble.empty()) {
458 features.addExternalPreamble(cit->second.preamble + "\n");
463 void InsetExternal::updateExternal(string const & format,
465 bool external_in_tmpdir) const
467 ExternalTemplate const * const et_ptr = getTemplatePtr(params_);
470 ExternalTemplate const & et = *et_ptr;
472 if (!et.automaticProduction)
475 ExternalTemplate::Formats::const_iterator cit =
476 et.formats.find(format);
477 if (cit == et.formats.end())
480 ExternalTemplate::FormatTemplate const & outputFormat = cit->second;
481 if (outputFormat.updateResult.empty())
484 string from_format = et.inputFormat;
485 if (from_format.empty())
488 string from_file = params_.filename.absFilename();
490 if (from_format == "*") {
491 if (from_file.empty())
494 // Try and ascertain the file format from its contents.
495 from_format = getExtFromContents(from_file);
496 if (from_format.empty())
500 string const to_format = outputFormat.updateFormat;
501 if (to_format.empty())
504 if (!converters.isReachable(from_format, to_format)) {
505 lyxerr << "InsetExternal::updateExternal. "
506 "Unable to convert from "
507 << from_format << " to " << to_format << endl;
511 if (external_in_tmpdir && !from_file.empty()) {
512 // We are running stuff through LaTeX
513 from_file = copyFileToDir(buf->tmppath, from_file);
514 if (from_file.empty())
518 string const to_file = doSubstitution(params_, buf,
519 outputFormat.updateResult);
521 FileInfo fi(from_file);
522 string abs_to_file = to_file;
523 if (!AbsolutePath(to_file))
524 abs_to_file = MakeAbsPath(to_file, OnlyPath(from_file));
525 FileInfo fi2(abs_to_file);
526 if (fi2.exist() && fi.exist() &&
527 difftime(fi2.getModificationTime(),
528 fi.getModificationTime()) >= 0) {
530 string const to_filebase = ChangeExtension(to_file, string());
531 converters.convert(buf, from_file, to_filebase,
532 from_format, to_format);
539 /// Substitute meta-variables in this string
540 string const doSubstitution(InsetExternal::Params const & params,
541 Buffer const * buffer, string const & s)
544 string const buffer_path = buffer ? buffer->filePath() : string();
545 string const filename = params.filename.outputFilename(buffer_path);
546 string const basename = ChangeExtension(filename, string());
547 string const filepath = OnlyPath(filename);
549 result = subst(s, "$$FName", filename);
550 result = subst(result, "$$Basename", basename);
551 result = subst(result, "$$FPath", filepath);
552 result = subst(result, "$$Tempname", params.tempname);
553 result = subst(result, "$$Sysdir", system_lyxdir);
555 // Handle the $$Contents(filename) syntax
556 if (contains(result, "$$Contents(\"")) {
558 string::size_type const pos = result.find("$$Contents(\"");
559 string::size_type const end = result.find("\")", pos);
560 string const file = result.substr(pos + 12, end - (pos + 12));
563 Path p(buffer->filePath());
564 if (!IsFileReadable(file))
565 Path p(buffer->tmppath);
566 if (IsFileReadable(file))
567 contents = GetFileContents(file);
569 contents = GetFileContents(file);
571 result = subst(result,
572 ("$$Contents(\"" + file + "\")").c_str(),
580 void editExternal(InsetExternal::Params const & params, Buffer const * buffer)
585 ExternalTemplate const * const et_ptr = getTemplatePtr(params);
588 ExternalTemplate const & et = *et_ptr;
590 if (et.editCommand.empty())
593 string const command = doSubstitution(params, buffer, et.editCommand);
595 Path p(buffer->filePath());
597 if (lyxerr.debugging()) {
598 lyxerr << "Executing '" << command << "' in '"
599 << buffer->filePath() << '\'' << endl;
601 call.startscript(Forkedcall::DontWait, command);
606 string const InsetExternalMailer::name_("external");
608 InsetExternalMailer::InsetExternalMailer(InsetExternal & inset)
613 string const InsetExternalMailer::inset2string(Buffer const & buffer) const
615 return params2string(inset_.params(), buffer);
619 void InsetExternalMailer::string2params(string const & in,
620 Buffer const & buffer,
621 InsetExternal::Params & params)
623 params = InsetExternal::Params();
628 istringstream data(STRCONV(in));
634 string const token = lex.getString();
639 // This is part of the inset proper that is usually swallowed
640 // by Buffer::readInset
643 string const token = lex.getString();
644 if (token != "External")
650 inset.read(&buffer, lex);
651 params = inset.params();
657 InsetExternalMailer::params2string(InsetExternal::Params const & params,
658 Buffer const & buffer)
661 inset.setParams(params, &buffer);
663 data << name_ << ' ';
664 inset.write(&buffer, data);
665 data << "\\end_inset\n";
666 return STRCONV(data.str());