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