]> git.lyx.org Git - lyx.git/blob - src/insets/insetexternal.C
d0eb36403bb5cc7f0197a45072163115543575e4
[lyx.git] / src / insets / insetexternal.C
1 /**
2  * \file insetexternal.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup Nielsen
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "insetexternal.h"
14 #include "insets/renderers.h"
15 #include "insets/ExternalSupport.h"
16 #include "insets/ExternalTemplate.h"
17
18 #include "buffer.h"
19 #include "BufferView.h"
20 #include "debug.h"
21 #include "funcrequest.h"
22 #include "gettext.h"
23 #include "LaTeXFeatures.h"
24 #include "latexrunparams.h"
25 #include "lyxlex.h"
26 #include "lyxrc.h"
27
28 #include "frontends/lyx_gui.h"
29
30 #include "support/lstrings.h"
31 #include "support/lyxlib.h"
32 #include "support/tostr.h"
33 #include "support/translator.h"
34
35 #include <boost/bind.hpp>
36
37 #include "support/std_sstream.h"
38
39 namespace support = lyx::support;
40 namespace external = lyx::external;
41
42 using std::endl;
43 using std::string;
44 using std::auto_ptr;
45 using std::istringstream;
46 using std::ostream;
47 using std::ostringstream;
48 using std::vector;
49
50
51 namespace lyx {
52 namespace graphics {
53
54 /// The translator between the DisplayType and the corresponding lyx string.
55 extern Translator<DisplayType, string> displayTranslator;
56
57 } // namespace graphics
58 } // namespace lyx
59
60
61 namespace {
62
63 lyx::graphics::DisplayType const defaultDisplayType = lyx::graphics::NoDisplay;
64
65 unsigned int defaultLyxScale = 100;
66
67 } // namespace anon
68
69
70 namespace lyx {
71 namespace external {
72
73 TempName::TempName()
74 {
75         tempname_ = support::tempName(string(), "lyxext");
76         support::unlink(tempname_);
77         // must have an extension for the converter code to work correctly.
78         tempname_ += ".tmp";
79 }
80
81
82 TempName::TempName(TempName const &)
83 {
84         tempname_ = TempName()();
85 }
86
87
88 TempName::~TempName()
89 {
90         support::unlink(tempname_);
91 }
92
93
94 TempName &
95 TempName::operator=(TempName const & other)
96 {
97         if (this != &other)
98                 tempname_ = TempName()();
99         return *this;
100 }
101
102 } // namespace external
103 } // namespace lyx
104
105
106 InsetExternalParams::InsetExternalParams()
107         : display(defaultDisplayType),
108           lyxscale(defaultLyxScale)
109 {}
110
111
112 void InsetExternalParams::settemplate(string const & name)
113 {
114         templatename_ = name;
115 }
116
117
118 void InsetExternalParams::write(Buffer const & buffer, ostream & os) const
119 {
120         os << "External\n"
121            << "\ttemplate " << templatename() << '\n';
122
123         if (!filename.empty())
124                 os << "\tfilename "
125                    << filename.outputFilename(buffer.filePath())
126                    << '\n';
127
128         if (display != defaultDisplayType)
129                 os << "\tdisplay "
130                    << lyx::graphics::displayTranslator.find(display)
131                    << '\n';
132
133         if (lyxscale != defaultLyxScale)
134                 os << "\tlyxscale " << tostr(lyxscale) << '\n';
135 }
136
137
138 bool InsetExternalParams::read(Buffer const & buffer, LyXLex & lex)
139 {
140         enum ExternalTags {
141                 EX_TEMPLATE = 1,
142                 EX_FILENAME,
143                 EX_DISPLAY,
144                 EX_LYXSCALE,
145                 EX_END
146         };
147
148         keyword_item external_tags[] = {
149                 { "\\end_inset",     EX_END },
150                 { "display",         EX_DISPLAY},
151                 { "filename",        EX_FILENAME},
152                 { "lyxscale",        EX_LYXSCALE},
153                 { "template",        EX_TEMPLATE }
154         };
155
156         pushpophelper pph(lex, external_tags, EX_END);
157
158         bool found_end  = false;
159         bool read_error = false;
160
161         while (lex.isOK()) {
162                 switch (lex.lex()) {
163                 case EX_TEMPLATE:
164                         lex.next();
165                         templatename_ = lex.getString();
166                         break;
167
168                 case EX_FILENAME: {
169                         lex.next();
170                         string const name = lex.getString();
171                         filename.set(name, buffer.filePath());
172                         break;
173                 }
174
175                 case EX_DISPLAY: {
176                         lex.next();
177                         string const name = lex.getString();
178                         display = lyx::graphics::displayTranslator.find(name);
179                         break;
180                 }
181
182                 case EX_LYXSCALE:
183                         lex.next();
184                         lyxscale = lex.getInteger();
185                         break;
186
187                 case EX_END:
188                         found_end = true;
189                         break;
190
191                 default:
192                         lex.printError("ExternalInset::read: "
193                                        "Wrong tag: $$Token");
194                         read_error = true;
195                         break;
196                 }
197
198                 if (found_end || read_error)
199                         break;
200         }
201
202         if (!found_end)
203                 lex.printError("ExternalInset::read: Missing \\end_inset.");
204
205         // This is a trick to make sure that the data are self-consistent.
206         settemplate(templatename_);
207
208         if (lyxerr.debugging(Debug::EXTERNAL)) {
209                 lyxerr  << "InsetExternalParams::read:\n";
210                 write(buffer, lyxerr);
211         }
212
213         return !read_error;
214 }
215
216
217 InsetExternal::InsetExternal()
218         : renderer_(new ButtonRenderer)
219 {}
220
221
222 InsetExternal::InsetExternal(InsetExternal const & other)
223         : InsetOld(other),
224           boost::signals::trackable(),
225           params_(other.params_),
226           renderer_(other.renderer_->clone())
227 {
228         GraphicRenderer * ptr =
229                 dynamic_cast<GraphicRenderer *>(renderer_.get());
230         if (ptr)
231                 ptr->connect(boost::bind(&InsetExternal::statusChanged, this));
232 }
233
234
235 auto_ptr<InsetBase> InsetExternal::clone() const
236 {
237         return auto_ptr<InsetBase>(new InsetExternal(*this));
238 }
239
240
241 InsetExternal::~InsetExternal()
242 {
243         InsetExternalMailer(*this).hideDialog();
244 }
245
246
247 void InsetExternal::statusChanged()
248 {
249         BufferView * const bv = renderer_->view();
250         if (bv)
251                 bv->updateInset(this);
252 }
253
254
255 dispatch_result InsetExternal::localDispatch(FuncRequest const & cmd)
256 {
257         switch (cmd.action) {
258
259         case LFUN_EXTERNAL_EDIT: {
260                 BOOST_ASSERT(cmd.view());
261
262                 Buffer const & buffer = *cmd.view()->buffer();
263                 InsetExternalParams p;
264                 InsetExternalMailer::string2params(cmd.argument, buffer, p);
265                 external::editExternal(p, buffer);
266                 return DISPATCHED_NOUPDATE;
267         }
268
269         case LFUN_INSET_MODIFY: {
270                 BOOST_ASSERT(cmd.view());
271
272                 Buffer const & buffer = *cmd.view()->buffer();
273                 InsetExternalParams p;
274                 InsetExternalMailer::string2params(cmd.argument, buffer, p);
275                 setParams(p, buffer);
276                 cmd.view()->updateInset(this);
277                 return DISPATCHED;
278         }
279
280         case LFUN_INSET_DIALOG_UPDATE:
281                 InsetExternalMailer(*this).updateDialog(cmd.view());
282                 return DISPATCHED;
283
284         case LFUN_MOUSE_RELEASE:
285         case LFUN_INSET_EDIT:
286                 InsetExternalMailer(*this).showDialog(cmd.view());
287                 return DISPATCHED;
288
289         default:
290                 return UNDISPATCHED;
291         }
292 }
293
294
295 void InsetExternal::metrics(MetricsInfo & mi, Dimension & dim) const
296 {
297         renderer_->metrics(mi, dim);
298         dim_ = dim;
299 }
300
301
302 void InsetExternal::draw(PainterInfo & pi, int x, int y) const
303 {
304         renderer_->draw(pi, x, y);
305 }
306
307
308 namespace {
309
310 lyx::graphics::Params get_grfx_params(InsetExternalParams const & eparams)
311 {
312         lyx::graphics::Params gparams;
313
314         gparams.filename = eparams.filename.absFilename();
315         gparams.scale = eparams.lyxscale;
316
317         gparams.display = eparams.display;
318         if (gparams.display == lyx::graphics::DefaultDisplay)
319                 gparams.display = lyxrc.display_graphics;
320         // Override the above if we're not using a gui
321         if (!lyx_gui::use_gui)
322                 gparams.display = lyx::graphics::NoDisplay;
323
324         return gparams;
325 }
326
327
328 string const getScreenLabel(InsetExternalParams const & params,
329                             Buffer const & buffer)
330 {
331         external::Template const * const ptr =
332                 external::getTemplatePtr(params);
333         if (!ptr)
334                 return support::bformat(_("External template %1$s is not installed"),
335                                         params.templatename());
336         return external::doSubstitution(params, buffer, ptr->guiName);
337 }
338
339 } // namespace anon
340
341
342 InsetExternalParams const & InsetExternal::params() const
343 {
344         return params_;
345 }
346
347
348 void InsetExternal::setParams(InsetExternalParams const & p,
349                               Buffer const & buffer)
350 {
351         // The stored params; what we would like to happen in an ideal world.
352         params_ = p;
353
354         // We display the inset as a button by default.
355         bool display_button = (!external::getTemplatePtr(params_) ||
356                                params_.filename.empty() ||
357                                params_.display == lyx::graphics::NoDisplay);
358
359         if (display_button) {
360                 ButtonRenderer * button_ptr =
361                         dynamic_cast<ButtonRenderer *>(renderer_.get());
362                 if (!button_ptr) {
363                         button_ptr = new ButtonRenderer;
364                         renderer_.reset(button_ptr);
365                 }
366
367                 button_ptr->update(getScreenLabel(params_, buffer), true);
368
369         } else {
370                 GraphicRenderer * graphic_ptr =
371                         dynamic_cast<GraphicRenderer *>(renderer_.get());
372                 if (!graphic_ptr) {
373                         graphic_ptr = new GraphicRenderer;
374                         graphic_ptr->connect(
375                                 boost::bind(&InsetExternal::statusChanged, this));
376                         renderer_.reset(graphic_ptr);
377                 }
378
379                 graphic_ptr->update(get_grfx_params(params_));
380         }
381 }
382
383
384 void InsetExternal::write(Buffer const & buffer, ostream & os) const
385 {
386         params_.write(buffer, os);
387 }
388
389
390 void InsetExternal::read(Buffer const & buffer, LyXLex & lex)
391 {
392         InsetExternalParams params;
393         if (params.read(buffer, lex))
394                 setParams(params, buffer);
395 }
396
397
398 int InsetExternal::latex(Buffer const & buf, ostream & os,
399                          LatexRunParams const & runparams) const
400 {
401         // "nice" means that the buffer is exported to LaTeX format but not
402         // run through the LaTeX compiler.
403         // If we're running through the LaTeX compiler, we should write the
404         // generated files in the bufer's temporary directory.
405         bool const external_in_tmpdir =
406                 lyxrc.use_tempdir && !buf.temppath().empty() && !runparams.nice;
407
408         // If the template has specified a PDFLaTeX output, then we try and
409         // use that.
410         if (runparams.flavor == LatexRunParams::PDFLATEX) {
411                 external::Template const * const et_ptr =
412                         external::getTemplatePtr(params_);
413                 if (!et_ptr)
414                         return 0;
415                 external::Template const & et = *et_ptr;
416
417                 external::Template::Formats::const_iterator cit =
418                         et.formats.find("PDFLaTeX");
419                 if (cit != et.formats.end())
420                         return external::writeExternal(params_, "PDFLaTeX",
421                                              buf, os, external_in_tmpdir);
422         }
423
424         return external::writeExternal(params_, "LaTeX",
425                                        buf, os, external_in_tmpdir);
426 }
427
428
429 int InsetExternal::ascii(Buffer const & buf, ostream & os, int) const
430 {
431         return external::writeExternal(params_, "Ascii", buf, os);
432 }
433
434
435 int InsetExternal::linuxdoc(Buffer const & buf, ostream & os) const
436 {
437         return external::writeExternal(params_, "LinuxDoc", buf, os);
438 }
439
440
441 int InsetExternal::docbook(Buffer const & buf, ostream & os, bool) const
442 {
443         return external::writeExternal(params_, "DocBook", buf, os);
444 }
445
446
447 void InsetExternal::validate(LaTeXFeatures & features) const
448 {
449         external::Template const * const et_ptr =
450                 external::getTemplatePtr(params_);
451         if (!et_ptr)
452                 return;
453         external::Template const & et = *et_ptr;
454
455         external::Template::Formats::const_iterator cit =
456                 et.formats.find("LaTeX");
457         if (cit == et.formats.end())
458                 return;
459
460         if (!cit->second.requirement.empty())
461                 features.require(cit->second.requirement);
462
463         external::TemplateManager & etm = external::TemplateManager::get();
464
465         vector<string>::const_iterator it  = cit->second.preambleNames.begin();
466         vector<string>::const_iterator end = cit->second.preambleNames.end();
467         for (; it != end; ++it) {
468                 string const preamble = etm.getPreambleDefByName(*it);
469                 if (!preamble.empty())
470                         features.addExternalPreamble(preamble);
471         }
472 }
473
474
475 string const InsetExternalMailer::name_("external");
476
477 InsetExternalMailer::InsetExternalMailer(InsetExternal & inset)
478         : inset_(inset)
479 {}
480
481
482 string const InsetExternalMailer::inset2string(Buffer const & buffer) const
483 {
484         return params2string(inset_.params(), buffer);
485 }
486
487
488 void InsetExternalMailer::string2params(string const & in,
489                                         Buffer const & buffer,
490                                         InsetExternalParams & params)
491 {
492         params = InsetExternalParams();
493
494         if (in.empty())
495                 return;
496
497         istringstream data(in);
498         LyXLex lex(0,0);
499         lex.setStream(data);
500
501         if (lex.isOK()) {
502                 lex.next();
503                 string const token = lex.getString();
504                 if (token != name_)
505                         return;
506         }
507
508         // This is part of the inset proper that is usually swallowed
509         // by Buffer::readInset
510         if (lex.isOK()) {
511                 lex.next();
512                 string const token = lex.getString();
513                 if (token != "External")
514                         return;
515         }
516
517         if (lex.isOK()) {
518                 params.read(buffer, lex);
519         }
520 }
521
522
523 string const
524 InsetExternalMailer::params2string(InsetExternalParams const & params,
525                                    Buffer const & buffer)
526 {
527         ostringstream data;
528         data << name_ << ' ';
529         params.write(buffer, data);
530         data << "\\end_inset\n";
531         return data.str();
532 }