]> git.lyx.org Git - lyx.git/blob - src/insets/insetinclude.C
Remove the inset and view member functions from PreviewedInset.
[lyx.git] / src / insets / insetinclude.C
1 /**
2  * \file insetinclude.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "insetinclude.h"
14
15 #include "buffer.h"
16 #include "buffer_funcs.h"
17 #include "bufferlist.h"
18 #include "bufferparams.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 "metricsinfo.h"
27
28 #include "frontends/Painter.h"
29
30 #include "graphics/PreviewedInset.h"
31 #include "graphics/PreviewImage.h"
32
33 #include "support/FileInfo.h"
34 #include "support/FileMonitor.h"
35 #include "support/filetools.h"
36 #include "support/lstrings.h" // contains
37 #include "support/tostr.h"
38
39 #include <boost/bind.hpp>
40
41 #include "support/std_sstream.h"
42
43 using lyx::support::AddName;
44 using lyx::support::ChangeExtension;
45 using lyx::support::contains;
46 using lyx::support::FileInfo;
47 using lyx::support::FileMonitor;
48 using lyx::support::GetFileContents;
49 using lyx::support::IsFileReadable;
50 using lyx::support::IsLyXFilename;
51 using lyx::support::MakeAbsPath;
52 using lyx::support::MakeDisplayPath;
53 using lyx::support::OnlyFilename;
54 using lyx::support::OnlyPath;
55 using lyx::support::subst;
56
57 using std::endl;
58 using std::string;
59 using std::auto_ptr;
60 using std::istringstream;
61 using std::ostream;
62 using std::ostringstream;
63
64
65 extern BufferList bufferlist;
66
67
68 class InsetInclude::PreviewImpl : public PreviewedInset {
69 public:
70         ///
71         PreviewImpl(InsetInclude const & p) : parent_(p) {}
72
73         ///
74         bool previewWanted(Buffer const &) const;
75         ///
76         string const latexString(Buffer const &) const;
77         ///
78         ///
79         bool monitoring() const { return monitor_.get(); }
80         ///
81         void startMonitoring(string const & file);
82         ///
83         void stopMonitoring() { monitor_.reset(); }
84
85 private:
86         /// Invoked by monitor_ should the parent file change.
87         void restartLoading();
88         ///
89         boost::scoped_ptr<FileMonitor> monitor_;
90         ///
91         InsetInclude const & parent_;
92 };
93
94
95 namespace {
96
97 string const uniqueID()
98 {
99         static unsigned int seed = 1000;
100         return "file" + tostr(++seed);
101 }
102
103 } // namespace anon
104
105
106 InsetInclude::InsetInclude(InsetCommandParams const & p)
107         : params_(p), include_label(uniqueID()),
108           preview_(new PreviewImpl(*this)),
109           set_label_(false)
110 {
111         preview_->connect(boost::bind(&InsetInclude::statusChanged, this));
112 }
113
114
115 InsetInclude::InsetInclude(InsetInclude const & other)
116         : InsetOld(other),
117           params_(other.params_),
118           include_label(other.include_label),
119           preview_(new PreviewImpl(*this)),
120           set_label_(other.set_label_)
121 {
122         preview_->connect(boost::bind(&InsetInclude::statusChanged, this));
123 }
124
125
126 InsetInclude::~InsetInclude()
127 {
128         InsetIncludeMailer mailer(*this);
129         mailer.hideDialog();
130 }
131
132
133 dispatch_result InsetInclude::localDispatch(FuncRequest const & cmd)
134 {
135         switch (cmd.action) {
136
137         case LFUN_INSET_MODIFY: {
138                 InsetCommandParams p;
139                 InsetIncludeMailer::string2params(cmd.argument, p);
140                 if (!p.getCmdName().empty()) {
141                         set(p, *cmd.view()->buffer());
142                         cmd.view()->updateInset(this);
143                 }
144                 return DISPATCHED;
145         }
146
147         case LFUN_INSET_DIALOG_UPDATE:
148                 InsetIncludeMailer(*this).updateDialog(cmd.view());
149                 return DISPATCHED;
150
151         case LFUN_MOUSE_RELEASE:
152                 if (button_.box().contains(cmd.x, cmd.y))
153                         InsetIncludeMailer(*this).showDialog(cmd.view());
154                 return DISPATCHED;
155
156         case LFUN_INSET_DIALOG_SHOW:
157                 InsetIncludeMailer(*this).showDialog(cmd.view());
158                 return DISPATCHED;
159
160         default:
161                 return InsetOld::localDispatch(cmd);
162         }
163 }
164
165
166 InsetCommandParams const & InsetInclude::params() const
167 {
168         return params_;
169 }
170
171
172 namespace {
173
174 /// the type of inclusion
175 enum Types {
176         INCLUDE = 0,
177         VERB = 1,
178         INPUT = 2,
179         VERBAST = 3
180 };
181
182
183 Types type(InsetCommandParams const & params)
184 {
185         string const command_name = params.getCmdName();
186
187         if (command_name == "input")
188                 return INPUT;
189         if  (command_name == "verbatiminput")
190                 return VERB;
191         if  (command_name == "verbatiminput*")
192                 return VERBAST;
193         return INCLUDE;
194 }
195
196
197 bool isVerbatim(InsetCommandParams const & params)
198 {
199         string const command_name = params.getCmdName();
200         return command_name == "verbatiminput" || 
201                 command_name == "verbatiminput*";
202 }
203
204 } // namespace anon
205
206
207 void InsetInclude::set(InsetCommandParams const & p, Buffer const & buffer)
208 {
209         params_ = p;
210         set_label_ = false;
211
212         if (preview_->monitoring())
213                 preview_->stopMonitoring();
214
215         if (PreviewedInset::activated() && type(params_) == INPUT)
216                 preview_->generatePreview(buffer);
217 }
218
219
220 auto_ptr<InsetBase> InsetInclude::clone() const
221 {
222         return auto_ptr<InsetBase>(new InsetInclude(*this));
223 }
224
225
226 void InsetInclude::write(Buffer const &, ostream & os) const
227 {
228         write(os);
229 }
230
231
232 void InsetInclude::write(ostream & os) const
233 {
234         os << "Include " << params_.getCommand() << '\n'
235            << "preview " << tostr(params_.preview()) << '\n';
236 }
237
238
239 void InsetInclude::read(Buffer const &, LyXLex & lex)
240 {
241         read(lex);
242 }
243
244
245 void InsetInclude::read(LyXLex & lex)
246 {
247         params_.read(lex);
248 }
249
250
251 string const InsetInclude::getScreenLabel(Buffer const &) const
252 {
253         string temp;
254
255         switch (type(params_)) {
256                 case INPUT: temp += _("Input"); break;
257                 case VERB: temp += _("Verbatim Input"); break;
258                 case VERBAST: temp += _("Verbatim Input*"); break;
259                 case INCLUDE: temp += _("Include"); break;
260         }
261
262         temp += ": ";
263
264         if (params_.getContents().empty())
265                 temp += "???";
266         else
267                 temp += OnlyFilename(params_.getContents());
268
269         return temp;
270 }
271
272
273 namespace {
274
275 string const masterFilename(Buffer const & buffer)
276 {
277         return buffer.fileName();
278 }
279
280
281 string const includedFilename(Buffer const & buffer,
282                               InsetCommandParams const & params)
283 {
284         return MakeAbsPath(params.getContents(),
285                            OnlyPath(masterFilename(buffer)));
286 }
287
288
289 /// return true if the file is or got loaded.
290 bool loadIfNeeded(Buffer const & buffer, InsetCommandParams const & params)
291 {
292         if (isVerbatim(params))
293                 return false;
294
295         string const included_file = includedFilename(buffer, params);
296         if (!IsLyXFilename(included_file))
297                 return false;
298
299         if (bufferlist.exists(included_file))
300                 return true;
301
302         // the readonly flag can/will be wrong, not anymore I think.
303         FileInfo finfo(included_file);
304         if (!finfo.isOK())
305                 return false;
306         return loadLyXFile(bufferlist.newBuffer(included_file),
307                            included_file);
308 }
309
310
311 } // namespace anon
312
313
314 int InsetInclude::latex(Buffer const & buffer, ostream & os,
315                         LatexRunParams const & runparams) const
316 {
317         string incfile(params_.getContents());
318
319         // Do nothing if no file name has been specified
320         if (incfile.empty())
321                 return 0;
322
323         string const included_file = includedFilename(buffer, params_);
324
325         if (loadIfNeeded(buffer, params_)) {
326                 Buffer * tmp = bufferlist.getBuffer(included_file);
327
328                 // FIXME: this should be a GUI warning
329                 if (tmp->params().textclass != buffer.params().textclass) {
330                         lyxerr << "WARNING: Included file `"
331                                << MakeDisplayPath(included_file)
332                                << "' has textclass `"
333                                << tmp->params().getLyXTextClass().name()
334                                << "' while parent file has textclass `"
335                                << buffer.params().getLyXTextClass().name()
336                                << "'." << endl;
337                         //return 0;
338                 }
339
340                 // write it to a file (so far the complete file)
341                 string writefile = ChangeExtension(included_file, ".tex");
342
343                 if (!buffer.temppath().empty() && !runparams.nice) {
344                         incfile = subst(incfile, '/','@');
345 #ifdef __EMX__
346                         incfile = subst(incfile, ':', '$');
347 #endif
348                         writefile = AddName(buffer.temppath(), incfile);
349                 } else
350                         writefile = included_file;
351                 writefile = ChangeExtension(writefile, ".tex");
352                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
353                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
354
355                 tmp->markDepClean(buffer.temppath());
356
357                 tmp->makeLaTeXFile(writefile,
358                                    OnlyPath(masterFilename(buffer)),
359                                    runparams, false);
360         }
361
362         if (isVerbatim(params_)) {
363                 os << '\\' << params_.getCmdName() << '{' << incfile << '}';
364         } else if (type(params_) == INPUT) {
365                 // \input wants file with extension (default is .tex)
366                 if (!IsLyXFilename(included_file)) {
367                         os << '\\' << params_.getCmdName() << '{' << incfile << '}';
368                 } else {
369                         os << '\\' << params_.getCmdName() << '{'
370                            << ChangeExtension(incfile, ".tex")
371                            <<  '}';
372                 }
373         } else {
374                 // \include don't want extension and demands that the
375                 // file really have .tex
376                 os << '\\' << params_.getCmdName() << '{'
377                    << ChangeExtension(incfile, string())
378                    << '}';
379         }
380
381         return 0;
382 }
383
384
385 int InsetInclude::ascii(Buffer const & buffer, ostream & os, int) const
386 {
387         if (isVerbatim(params_))
388                 os << GetFileContents(includedFilename(buffer, params_));
389         return 0;
390 }
391
392
393 int InsetInclude::linuxdoc(Buffer const & buffer, ostream & os) const
394 {
395         string incfile(params_.getContents());
396
397         // Do nothing if no file name has been specified
398         if (incfile.empty())
399                 return 0;
400
401         string const included_file = includedFilename(buffer, params_);
402
403         if (loadIfNeeded(buffer, params_)) {
404                 Buffer * tmp = bufferlist.getBuffer(included_file);
405
406                 // write it to a file (so far the complete file)
407                 string writefile = ChangeExtension(included_file, ".sgml");
408                 if (!buffer.temppath().empty() && !buffer.niceFile()) {
409                         incfile = subst(incfile, '/','@');
410                         writefile = AddName(buffer.temppath(), incfile);
411                 } else
412                         writefile = included_file;
413
414                 if (IsLyXFilename(included_file))
415                         writefile = ChangeExtension(writefile, ".sgml");
416
417                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
418                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
419
420                 tmp->makeLinuxDocFile(writefile, buffer.niceFile(), true);
421         }
422
423         if (isVerbatim(params_)) {
424                 os << "<![CDATA["
425                    << GetFileContents(included_file)
426                    << "]]>";
427         } else
428                 os << '&' << include_label << ';';
429
430         return 0;
431 }
432
433
434 int InsetInclude::docbook(Buffer const & buffer, ostream & os,
435                           bool /*mixcont*/) const
436 {
437         string incfile(params_.getContents());
438
439         // Do nothing if no file name has been specified
440         if (incfile.empty())
441                 return 0;
442
443         string const included_file = includedFilename(buffer, params_);
444
445         if (loadIfNeeded(buffer, params_)) {
446                 Buffer * tmp = bufferlist.getBuffer(included_file);
447
448                 // write it to a file (so far the complete file)
449                 string writefile = ChangeExtension(included_file, ".sgml");
450                 if (!buffer.temppath().empty() && !buffer.niceFile()) {
451                         incfile = subst(incfile, '/','@');
452                         writefile = AddName(buffer.temppath(), incfile);
453                 } else
454                         writefile = included_file;
455                 if (IsLyXFilename(included_file))
456                         writefile = ChangeExtension(writefile, ".sgml");
457
458                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
459                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
460
461                 tmp->makeDocBookFile(writefile, buffer.niceFile(), true);
462         }
463
464         if (isVerbatim(params_)) {
465                 os << "<inlinegraphic fileref=\""
466                    << '&' << include_label << ';'
467                    << "\" format=\"linespecific\">";
468         } else
469                 os << '&' << include_label << ';';
470
471         return 0;
472 }
473
474
475 void InsetInclude::validate(LaTeXFeatures & features) const
476 {
477         string incfile(params_.getContents());
478         string writefile;
479
480         Buffer const & buffer = features.buffer();
481
482         string const included_file = includedFilename(buffer, params_);
483
484         if (!buffer.temppath().empty() &&
485             !buffer.niceFile() &&
486             !isVerbatim(params_)) {
487                 incfile = subst(incfile, '/','@');
488                 writefile = AddName(buffer.temppath(), incfile);
489         } else
490                 writefile = included_file;
491
492         if (IsLyXFilename(included_file))
493                 writefile = ChangeExtension(writefile, ".sgml");
494
495         features.includeFile(include_label, writefile);
496
497         if (isVerbatim(params_))
498                 features.require("verbatim");
499
500         // Here we must do the fun stuff...
501         // Load the file in the include if it needs
502         // to be loaded:
503         if (loadIfNeeded(buffer, params_)) {
504                 // a file got loaded
505                 Buffer * const tmp = bufferlist.getBuffer(included_file);
506                 if (tmp) {
507                         tmp->niceFile() = buffer.niceFile();
508                         tmp->validate(features);
509                 }
510         }
511 }
512
513
514 void InsetInclude::getLabelList(Buffer const & buffer,
515                                 std::vector<string> & list) const
516 {
517         if (loadIfNeeded(buffer, params_)) {
518                 string const included_file = includedFilename(buffer, params_);
519                 Buffer * tmp = bufferlist.getBuffer(included_file);
520                 tmp->setParentName("");
521                 tmp->getLabelList(list);
522                 tmp->setParentName(masterFilename(buffer));
523         }
524 }
525
526
527 void InsetInclude::fillWithBibKeys(Buffer const & buffer,
528                                    std::vector<std::pair<string,string> > & keys) const
529 {
530         if (loadIfNeeded(buffer, params_)) {
531                 string const included_file = includedFilename(buffer, params_);
532                 Buffer * tmp = bufferlist.getBuffer(included_file);
533                 tmp->setParentName("");
534                 tmp->fillWithBibKeys(keys);
535                 tmp->setParentName(masterFilename(buffer));
536         }
537 }
538
539
540 void InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
541 {
542         Buffer const * buffer_ptr = mi.base.bv ? mi.base.bv->buffer() : 0;
543         if (buffer_ptr && preview_->previewReady(*buffer_ptr)) {
544                 dim.asc = preview_->pimage()->ascent();
545                 dim.des = preview_->pimage()->descent();
546                 dim.wid = preview_->pimage()->width();
547         } else {
548                 if (!set_label_) {
549                         set_label_ = true;
550                         button_.update(getScreenLabel(*mi.base.bv->buffer()),
551                                        editable() != NOT_EDITABLE);
552                 }
553                 button_.metrics(mi, dim);
554         }
555         int center_indent = type(params_) == INPUT ?
556                 0 : (mi.base.textwidth - dim.wid) / 2;
557         Box b(center_indent, center_indent + dim.wid, -dim.asc, dim.des);
558         button_.setBox(b);
559
560         dim.wid = mi.base.textwidth;
561         dim_ = dim;
562 }
563
564
565 void InsetInclude::draw(PainterInfo & pi, int x, int y) const
566 {
567         cache(pi.base.bv);
568         Buffer const * buffer_ptr = pi.base.bv ? pi.base.bv->buffer() : 0;
569         bool const use_preview = buffer_ptr && preview_->previewReady(*buffer_ptr);
570
571         if (!use_preview) {
572                 button_.draw(pi, x + button_.box().x1, y);
573                 return;
574         }
575
576         if (!preview_->monitoring()) {
577                 string const included_file =
578                         includedFilename(*view()->buffer(), params_);
579                 preview_->startMonitoring(included_file);
580         }
581
582         pi.pain.image(x + button_.box().x1, y - dim_.asc, dim_.wid, dim_.height(),
583                             *(preview_->pimage()->image()));
584 }
585
586
587 BufferView * InsetInclude::view() const
588 {
589         return button_.view();
590 }
591
592
593 //
594 // preview stuff
595 //
596
597 void InsetInclude::statusChanged() const
598 {
599         if (view())
600                 view()->updateInset(this);
601 }
602
603
604 void InsetInclude::addPreview(lyx::graphics::PreviewLoader & ploader) const
605 {
606         preview_->addPreview(ploader);
607 }
608
609
610 bool InsetInclude::PreviewImpl::previewWanted(Buffer const & buffer) const
611 {
612         string const included_file = includedFilename(buffer, parent_.params());
613
614         return type(parent_.params_) == INPUT &&
615                 parent_.params_.preview() &&
616                 IsFileReadable(included_file);
617 }
618
619
620 string const InsetInclude::PreviewImpl::latexString(Buffer const & buffer) const
621 {
622         ostringstream os;
623         LatexRunParams runparams;
624         runparams.flavor = LatexRunParams::LATEX;
625         parent_.latex(buffer, os, runparams);
626
627         return os.str();
628 }
629
630
631 void InsetInclude::PreviewImpl::startMonitoring(string const & file)
632 {
633         monitor_.reset(new FileMonitor(file, 2000));
634         monitor_->connect(boost::bind(&PreviewImpl::restartLoading, this));
635         monitor_->start();
636 }
637
638
639 void InsetInclude::PreviewImpl::restartLoading()
640 {
641         BufferView * const view = parent_.view();
642         if (!view)
643                 return;
644         view->updateInset(&parent_);
645         if (view->buffer()) {
646                 Buffer const & buffer = *view->buffer();
647                 removePreview(buffer);
648                 generatePreview(buffer);
649         }
650 }
651
652
653 string const InsetIncludeMailer::name_("include");
654
655 InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
656         : inset_(inset)
657 {}
658
659
660 string const InsetIncludeMailer::inset2string(Buffer const &) const
661 {
662         return params2string(inset_.params());
663 }
664
665
666 void InsetIncludeMailer::string2params(string const & in,
667                                        InsetCommandParams & params)
668 {
669         params = InsetCommandParams();
670
671         if (in.empty())
672                 return;
673
674         istringstream data(in);
675         LyXLex lex(0,0);
676         lex.setStream(data);
677
678         if (lex.isOK()) {
679                 lex.next();
680                 string const token = lex.getString();
681                 if (token != name_)
682                         return;
683         }
684
685         // This is part of the inset proper that is usually swallowed
686         // by Buffer::readInset
687         if (lex.isOK()) {
688                 lex.next();
689                 string const token = lex.getString();
690                 if (token != "Include")
691                         return;
692         }
693
694         if (lex.isOK()) {
695                 InsetInclude inset(params);
696                 inset.read(lex);
697                 params = inset.params();
698         }
699 }
700
701
702 string const
703 InsetIncludeMailer::params2string(InsetCommandParams const & params)
704 {
705         InsetInclude inset(params);
706         ostringstream data;
707         data << name_ << ' ';
708         inset.write(data);
709         data << "\\end_inset\n";
710         return data.str();
711 }