]> git.lyx.org Git - lyx.git/blob - src/insets/insetexternal.C
14b95c7fdee304bc7dceffb00d619bdb15cfc9fc
[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/ExternalSupport.h"
15 #include "insets/ExternalTemplate.h"
16 #include "insets/render_button.h"
17 #include "insets/render_graphic.h"
18
19 #include "buffer.h"
20 #include "BufferView.h"
21 #include "debug.h"
22 #include "funcrequest.h"
23 #include "gettext.h"
24 #include "LaTeXFeatures.h"
25 #include "latexrunparams.h"
26 #include "lyxlex.h"
27 #include "lyxrc.h"
28 #include "metricsinfo.h"
29
30 #include "frontends/lyx_gui.h"
31 #include "frontends/LyXView.h"
32
33 #include "support/lstrings.h"
34 #include "support/lyxlib.h"
35 #include "support/tostr.h"
36 #include "support/translator.h"
37
38 #include <boost/bind.hpp>
39
40 #include "support/std_sstream.h"
41
42 namespace support = lyx::support;
43 namespace external = lyx::external;
44
45 using std::endl;
46 using std::string;
47 using std::auto_ptr;
48 using std::istringstream;
49 using std::ostream;
50 using std::ostringstream;
51 using std::vector;
52
53
54 namespace {
55
56 lyx::graphics::DisplayType const defaultDisplayType = lyx::graphics::NoDisplay;
57
58 unsigned int defaultLyxScale = 100;
59
60 } // namespace anon
61
62
63 namespace lyx {
64 namespace external {
65
66 TempName::TempName()
67 {
68         tempname_ = support::tempName(string(), "lyxext");
69         support::unlink(tempname_);
70         // must have an extension for the converter code to work correctly.
71         tempname_ += ".tmp";
72 }
73
74
75 TempName::TempName(TempName const &)
76 {
77         tempname_ = TempName()();
78 }
79
80
81 TempName::~TempName()
82 {
83         support::unlink(tempname_);
84 }
85
86
87 TempName &
88 TempName::operator=(TempName const & other)
89 {
90         if (this != &other)
91                 tempname_ = TempName()();
92         return *this;
93 }
94
95 } // namespace external
96 } // namespace lyx
97
98
99 InsetExternalParams::InsetExternalParams()
100         : display(defaultDisplayType),
101           lyxscale(defaultLyxScale)
102 {}
103
104
105 namespace {
106
107 template <typename T>
108 void clearIfNotFound(T & data, external::TransformID value,
109                      vector<external::TransformID> const & ids)
110 {
111         typedef vector<external::TransformID>::const_iterator
112                 const_iterator;
113
114         const_iterator it  = ids.begin();
115         const_iterator end = ids.end();
116         it = std::find(it, end, value);
117         if (it == end)
118                 data = T();
119 }
120
121 } // namespace anon
122
123
124 void InsetExternalParams::settemplate(string const & name)
125 {
126         templatename_ = name;
127
128         external::TemplateManager const & etm =
129                 external::TemplateManager::get();
130         external::Template const * const et = etm.getTemplateByName(name);
131         if (!et)
132                 // Be safe. Don't lose data.
133                 return;
134
135         // Ascertain which transforms the template supports.
136         // Empty all those that it doesn't.
137         vector<external::TransformID> const & ids = et->transformIds;
138         clearIfNotFound(clipdata,     external::Clip,   ids);
139         clearIfNotFound(extradata,    external::Extra,  ids);
140         clearIfNotFound(resizedata,   external::Resize, ids);
141         clearIfNotFound(rotationdata, external::Rotate, ids);
142 }
143
144
145 void InsetExternalParams::write(Buffer const & buffer, ostream & os) const
146 {
147         os << "External\n"
148            << "\ttemplate " << templatename() << '\n';
149
150         if (!filename.empty())
151                 os << "\tfilename "
152                    << filename.outputFilename(buffer.filePath())
153                    << '\n';
154
155         if (display != defaultDisplayType)
156                 os << "\tdisplay "
157                    << lyx::graphics::displayTranslator().find(display)
158                    << '\n';
159
160         if (lyxscale != defaultLyxScale)
161                 os << "\tlyxscale " << tostr(lyxscale) << '\n';
162
163         if (!clipdata.bbox.empty())
164                 os << "\tboundingBox " << clipdata.bbox << '\n';
165         if (clipdata.clip)
166                 os << "\tclip\n";
167
168         external::ExtraData::const_iterator it  = extradata.begin();
169         external::ExtraData::const_iterator end = extradata.end();
170         for (; it != end; ++it) {
171                 if (!it->second.empty())
172                         os << "\textra " << it->first << " \""
173                            << it->second << "\"\n";
174         }
175
176         if (!rotationdata.no_rotation()) {
177                 os << "\trotateAngle " << rotationdata.angle() << '\n';
178                 if (rotationdata.origin() != external::RotationData::DEFAULT)
179                         os << "\trotateOrigin "
180                            << rotationdata.originString() << '\n';
181         }
182
183         if (!resizedata.no_resize()) {
184                 using support::float_equal;
185
186                 if (!float_equal(resizedata.scale, 0.0, 0.05)) {
187                         if (!float_equal(resizedata.scale, 100.0, 0.05))
188                                 os << "\tscale "
189                                    << resizedata.scale << '\n';
190                 } else {
191                         if (!resizedata.width.zero())
192                                 os << "\twidth "
193                                    << resizedata.width.asString() << '\n';
194                         if (!resizedata.height.zero())
195                                 os << "\theight "
196                                    << resizedata.height.asString() << '\n';
197                 }
198                 if (resizedata.keepAspectRatio)
199                         os << "\tkeepAspectRatio\n";
200         }
201 }
202
203
204 bool InsetExternalParams::read(Buffer const & buffer, LyXLex & lex)
205 {
206         enum ExternalTags {
207                 EX_TEMPLATE = 1,
208                 EX_FILENAME,
209                 EX_DISPLAY,
210                 EX_LYXSCALE,
211                 EX_BOUNDINGBOX,
212                 EX_CLIP,
213                 EX_EXTRA,
214                 EX_HEIGHT,
215                 EX_KEEPASPECTRATIO,
216                 EX_ROTATEANGLE,
217                 EX_ROTATEORIGIN,
218                 EX_SCALE,
219                 EX_WIDTH,
220                 EX_END
221         };
222
223         keyword_item external_tags[] = {
224                 { "\\end_inset",     EX_END },
225                 { "boundingBox",     EX_BOUNDINGBOX },
226                 { "clip",            EX_CLIP },
227                 { "display",         EX_DISPLAY},
228                 { "extra",           EX_EXTRA },
229                 { "filename",        EX_FILENAME},
230                 { "height",          EX_HEIGHT },
231                 { "keepAspectRatio", EX_KEEPASPECTRATIO },
232                 { "lyxscale",        EX_LYXSCALE},
233                 { "rotateAngle",     EX_ROTATEANGLE },
234                 { "rotateOrigin",    EX_ROTATEORIGIN },
235                 { "scale",           EX_SCALE },
236                 { "template",        EX_TEMPLATE },
237                 { "width",           EX_WIDTH }
238         };
239
240         pushpophelper pph(lex, external_tags, EX_END);
241
242         bool found_end  = false;
243         bool read_error = false;
244
245         while (lex.isOK()) {
246                 switch (lex.lex()) {
247                 case EX_TEMPLATE:
248                         lex.next();
249                         templatename_ = lex.getString();
250                         break;
251
252                 case EX_FILENAME: {
253                         lex.next();
254                         string const name = lex.getString();
255                         filename.set(name, buffer.filePath());
256                         break;
257                 }
258
259                 case EX_DISPLAY: {
260                         lex.next();
261                         string const name = lex.getString();
262                         display = lyx::graphics::displayTranslator().find(name);
263                         break;
264                 }
265
266                 case EX_LYXSCALE:
267                         lex.next();
268                         lyxscale = lex.getInteger();
269                         break;
270
271                 case EX_BOUNDINGBOX:
272                         lex.next();
273                         clipdata.bbox.xl = lex.getInteger();
274                         lex.next();
275                         clipdata.bbox.yb = lex.getInteger();
276                         lex.next();
277                         clipdata.bbox.xr = lex.getInteger();
278                         lex.next();
279                         clipdata.bbox.yt = lex.getInteger();
280                         break;
281
282                 case EX_CLIP:
283                         clipdata.clip = true;
284                         break;
285
286                 case EX_EXTRA: {
287                         lex.next();
288                         string const name = lex.getString();
289                         lex.next();
290                         extradata.set(name, lex.getString());
291                         break;
292                 }
293
294                 case EX_HEIGHT:
295                         lex.next();
296                         resizedata.height = LyXLength(lex.getString());
297                         break;
298
299                 case EX_KEEPASPECTRATIO:
300                         resizedata.keepAspectRatio = true;
301                         break;
302
303                 case EX_ROTATEANGLE:
304                         lex.next();
305                         rotationdata.angle(lex.getFloat());
306                         break;
307
308                 case EX_ROTATEORIGIN:
309                         lex.next();
310                         rotationdata.origin(lex.getString());
311                         break;
312
313                 case EX_SCALE:
314                         lex.next();
315                         resizedata.scale = lex.getFloat();
316                         break;
317
318                 case EX_WIDTH:
319                         lex.next();
320                         resizedata.width = LyXLength(lex.getString());
321                         break;
322
323                 case EX_END:
324                         found_end = true;
325                         break;
326
327                 default:
328                         lex.printError("ExternalInset::read: "
329                                        "Wrong tag: $$Token");
330                         read_error = true;
331                         break;
332                 }
333
334                 if (found_end || read_error)
335                         break;
336         }
337
338         if (!found_end)
339                 lex.printError("ExternalInset::read: Missing \\end_inset.");
340
341         // This is a trick to make sure that the data are self-consistent.
342         settemplate(templatename_);
343
344         if (lyxerr.debugging(Debug::EXTERNAL)) {
345                 lyxerr  << "InsetExternalParams::read:\n";
346                 write(buffer, lyxerr);
347         }
348
349         return !read_error;
350 }
351
352
353 InsetExternal::InsetExternal()
354         : renderer_(new RenderButton)
355 {}
356
357
358 InsetExternal::InsetExternal(InsetExternal const & other)
359         : InsetOld(other),
360           boost::signals::trackable(),
361           params_(other.params_),
362           renderer_(other.renderer_->clone())
363 {
364         RenderGraphic * ptr =
365                 dynamic_cast<RenderGraphic *>(renderer_.get());
366         if (ptr)
367                 ptr->connect(boost::bind(&InsetExternal::statusChanged, this));
368 }
369
370
371 auto_ptr<InsetBase> InsetExternal::clone() const
372 {
373         return auto_ptr<InsetBase>(new InsetExternal(*this));
374 }
375
376
377 InsetExternal::~InsetExternal()
378 {
379         InsetExternalMailer(*this).hideDialog();
380 }
381
382
383 void InsetExternal::cache(BufferView * view) const
384 {
385         BOOST_ASSERT(view);
386         view_ = view->owner()->view();
387 }
388
389 BufferView * InsetExternal::view() const
390 {
391         return view_.lock().get();
392 }
393
394
395 void InsetExternal::statusChanged() const
396 {
397         BufferView * const bv = view();
398         if (bv)
399                 bv->updateInset(this);
400 }
401
402
403 dispatch_result InsetExternal::localDispatch(FuncRequest const & cmd)
404 {
405         switch (cmd.action) {
406
407         case LFUN_EXTERNAL_EDIT: {
408                 BOOST_ASSERT(cmd.view());
409
410                 Buffer const & buffer = *cmd.view()->buffer();
411                 InsetExternalParams p;
412                 InsetExternalMailer::string2params(cmd.argument, buffer, p);
413                 external::editExternal(p, buffer);
414                 return DISPATCHED_NOUPDATE;
415         }
416
417         case LFUN_INSET_MODIFY: {
418                 BOOST_ASSERT(cmd.view());
419
420                 Buffer const & buffer = *cmd.view()->buffer();
421                 InsetExternalParams p;
422                 InsetExternalMailer::string2params(cmd.argument, buffer, p);
423                 setParams(p, buffer);
424                 cmd.view()->updateInset(this);
425                 return DISPATCHED;
426         }
427
428         case LFUN_INSET_DIALOG_UPDATE:
429                 InsetExternalMailer(*this).updateDialog(cmd.view());
430                 return DISPATCHED;
431
432         case LFUN_MOUSE_RELEASE:
433         case LFUN_INSET_EDIT:
434                 InsetExternalMailer(*this).showDialog(cmd.view());
435                 return DISPATCHED;
436
437         default:
438                 return UNDISPATCHED;
439         }
440 }
441
442
443 void InsetExternal::metrics(MetricsInfo & mi, Dimension & dim) const
444 {
445         renderer_->metrics(mi, dim);
446         dim_ = dim;
447 }
448
449
450 void InsetExternal::draw(PainterInfo & pi, int x, int y) const
451 {
452         cache(pi.base.bv);
453         renderer_->draw(pi, x, y);
454 }
455
456
457 namespace {
458
459 lyx::graphics::Params get_grfx_params(InsetExternalParams const & eparams)
460 {
461         lyx::graphics::Params gparams;
462
463         gparams.filename = eparams.filename.absFilename();
464         gparams.scale = eparams.lyxscale;
465         if (eparams.clipdata.clip)
466                 gparams.bb = eparams.clipdata.bbox;
467         gparams.angle = eparams.rotationdata.angle();
468
469         gparams.display = eparams.display;
470         if (gparams.display == lyx::graphics::DefaultDisplay)
471                 gparams.display = lyxrc.display_graphics;
472         // Override the above if we're not using a gui
473         if (!lyx_gui::use_gui)
474                 gparams.display = lyx::graphics::NoDisplay;
475
476         return gparams;
477 }
478
479
480 string const getScreenLabel(InsetExternalParams const & params,
481                             Buffer const & buffer)
482 {
483         external::Template const * const ptr =
484                 external::getTemplatePtr(params);
485         if (!ptr)
486                 return support::bformat(_("External template %1$s is not installed"),
487                                         params.templatename());
488         return external::doSubstitution(params, buffer, ptr->guiName);
489 }
490
491 } // namespace anon
492
493
494 InsetExternalParams const & InsetExternal::params() const
495 {
496         return params_;
497 }
498
499
500 void InsetExternal::setParams(InsetExternalParams const & p,
501                               Buffer const & buffer)
502 {
503         // The stored params; what we would like to happen in an ideal world.
504         params_ = p;
505
506         // We display the inset as a button by default.
507         bool display_button = (!external::getTemplatePtr(params_) ||
508                                params_.filename.empty() ||
509                                params_.display == lyx::graphics::NoDisplay);
510
511         if (display_button) {
512                 RenderButton * button_ptr =
513                         dynamic_cast<RenderButton *>(renderer_.get());
514                 if (!button_ptr) {
515                         button_ptr = new RenderButton;
516                         renderer_.reset(button_ptr);
517                 }
518
519                 button_ptr->update(getScreenLabel(params_, buffer), true);
520
521         } else {
522                 RenderGraphic * graphic_ptr =
523                         dynamic_cast<RenderGraphic *>(renderer_.get());
524                 if (!graphic_ptr) {
525                         graphic_ptr = new RenderGraphic;
526                         graphic_ptr->connect(
527                                 boost::bind(&InsetExternal::statusChanged, this));
528                         renderer_.reset(graphic_ptr);
529                 }
530
531                 graphic_ptr->update(get_grfx_params(params_));
532         }
533 }
534
535
536 void InsetExternal::write(Buffer const & buffer, ostream & os) const
537 {
538         params_.write(buffer, os);
539 }
540
541
542 void InsetExternal::read(Buffer const & buffer, LyXLex & lex)
543 {
544         InsetExternalParams params;
545         if (params.read(buffer, lex))
546                 setParams(params, buffer);
547 }
548
549
550 int InsetExternal::latex(Buffer const & buf, ostream & os,
551                          LatexRunParams const & runparams) const
552 {
553         // "nice" means that the buffer is exported to LaTeX format but not
554         // run through the LaTeX compiler.
555         // If we're running through the LaTeX compiler, we should write the
556         // generated files in the bufer's temporary directory.
557         bool const external_in_tmpdir =
558                 lyxrc.use_tempdir && !buf.temppath().empty() && !runparams.nice;
559
560         // If the template has specified a PDFLaTeX output, then we try and
561         // use that.
562         if (runparams.flavor == LatexRunParams::PDFLATEX) {
563                 external::Template const * const et_ptr =
564                         external::getTemplatePtr(params_);
565                 if (!et_ptr)
566                         return 0;
567                 external::Template const & et = *et_ptr;
568
569                 external::Template::Formats::const_iterator cit =
570                         et.formats.find("PDFLaTeX");
571                 if (cit != et.formats.end())
572                         return external::writeExternal(params_, "PDFLaTeX",
573                                              buf, os, external_in_tmpdir);
574         }
575
576         return external::writeExternal(params_, "LaTeX",
577                                        buf, os, external_in_tmpdir);
578 }
579
580
581 int InsetExternal::ascii(Buffer const & buf, ostream & os, int) const
582 {
583         return external::writeExternal(params_, "Ascii", buf, os);
584 }
585
586
587 int InsetExternal::linuxdoc(Buffer const & buf, ostream & os) const
588 {
589         return external::writeExternal(params_, "LinuxDoc", buf, os);
590 }
591
592
593 int InsetExternal::docbook(Buffer const & buf, ostream & os, bool) const
594 {
595         return external::writeExternal(params_, "DocBook", buf, os);
596 }
597
598
599 void InsetExternal::validate(LaTeXFeatures & features) const
600 {
601         external::Template const * const et_ptr =
602                 external::getTemplatePtr(params_);
603         if (!et_ptr)
604                 return;
605         external::Template const & et = *et_ptr;
606
607         external::Template::Formats::const_iterator cit =
608                 et.formats.find("LaTeX");
609         if (cit == et.formats.end())
610                 return;
611
612         if (!cit->second.requirement.empty())
613                 features.require(cit->second.requirement);
614
615         external::TemplateManager & etm = external::TemplateManager::get();
616
617         vector<string>::const_iterator it  = cit->second.preambleNames.begin();
618         vector<string>::const_iterator end = cit->second.preambleNames.end();
619         for (; it != end; ++it) {
620                 string const preamble = etm.getPreambleDefByName(*it);
621                 if (!preamble.empty())
622                         features.addExternalPreamble(preamble);
623         }
624 }
625
626
627 string const InsetExternalMailer::name_("external");
628
629 InsetExternalMailer::InsetExternalMailer(InsetExternal & inset)
630         : inset_(inset)
631 {}
632
633
634 string const InsetExternalMailer::inset2string(Buffer const & buffer) const
635 {
636         return params2string(inset_.params(), buffer);
637 }
638
639
640 void InsetExternalMailer::string2params(string const & in,
641                                         Buffer const & buffer,
642                                         InsetExternalParams & params)
643 {
644         params = InsetExternalParams();
645
646         if (in.empty())
647                 return;
648
649         istringstream data(in);
650         LyXLex lex(0,0);
651         lex.setStream(data);
652
653         if (lex.isOK()) {
654                 lex.next();
655                 string const token = lex.getString();
656                 if (token != name_)
657                         return;
658         }
659
660         // This is part of the inset proper that is usually swallowed
661         // by Buffer::readInset
662         if (lex.isOK()) {
663                 lex.next();
664                 string const token = lex.getString();
665                 if (token != "External")
666                         return;
667         }
668
669         if (lex.isOK()) {
670                 params.read(buffer, lex);
671         }
672 }
673
674
675 string const
676 InsetExternalMailer::params2string(InsetExternalParams const & params,
677                                    Buffer const & buffer)
678 {
679         ostringstream data;
680         data << name_ << ' ';
681         params.write(buffer, data);
682         data << "\\end_inset\n";
683         return data.str();
684 }