]> git.lyx.org Git - lyx.git/blob - src/insets/insetinclude.C
Finish the migration from namespace grfx to lyx::graphics.
[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 #include "buffer.h"
15 #include "buffer_funcs.h"
16 #include "bufferlist.h"
17 #include "BufferView.h"
18 #include "debug.h"
19 #include "funcrequest.h"
20 #include "gettext.h"
21 #include "LaTeXFeatures.h"
22 #include "latexrunparams.h"
23 #include "Lsstream.h"
24 #include "lyxlex.h"
25 #include "lyxrc.h"
26 #include "metricsinfo.h"
27 #include "dimension.h"
28
29 #include "frontends/Dialogs.h"
30 #include "frontends/LyXView.h"
31 #include "frontends/Painter.h"
32
33 #include "support/filetools.h"
34 #include "support/FileInfo.h"
35 #include "support/FileMonitor.h"
36 #include "support/lstrings.h" // contains
37 #include "support/tostr.h"
38
39 #include "graphics/PreviewedInset.h"
40 #include "graphics/PreviewImage.h"
41
42 #include <boost/bind.hpp>
43
44 #include <cstdlib>
45
46 using namespace lyx::support;
47
48 using std::ostream;
49 using std::endl;
50 using std::vector;
51 using std::pair;
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         : Inset(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         case LFUN_INSET_EDIT:
150                 InsetIncludeMailer(*this).showDialog(cmd.view());
151                 return DISPATCHED;
152
153         default:
154                 return Inset::localDispatch(cmd);
155         }
156 }
157
158
159 InsetInclude::Params const & InsetInclude::params() const
160 {
161         return params_;
162 }
163
164
165 bool InsetInclude::Params::operator==(Params const & o) const
166 {
167         return cparams == o.cparams && flag == o.flag &&
168             masterFilename_ == o.masterFilename_;
169 }
170
171
172 bool InsetInclude::Params::operator!=(Params const & o) const
173 {
174         return !(*this == o);
175 }
176
177
178 void InsetInclude::set(Params const & p)
179 {
180         params_ = p;
181
182         string command;
183
184         switch (params_.flag) {
185                 case INCLUDE:
186                         command="include";
187                         break;
188                 case VERB:
189                         command="verbatiminput";
190                         break;
191                 case INPUT:
192                         command="input";
193                         break;
194                 case VERBAST:
195                         command="verbatiminput*";
196                         break;
197         }
198
199         params_.cparams.setCmdName(command);
200
201         if (preview_->monitoring())
202                 preview_->stopMonitoring();
203
204         if (lyx::graphics::PreviewedInset::activated() && params_.flag == INPUT)
205                 preview_->generatePreview();
206 }
207
208
209 InsetBase * InsetInclude::clone() const
210 {
211         //Params p(params_);
212         //p.masterFilename_ = buffer.fileName();
213 #warning FIXME: broken cross-doc copy/paste - must fix
214
215         return new InsetInclude(params_);
216 }
217
218
219 void InsetInclude::write(Buffer const *, ostream & os) const
220 {
221         os << "Include " << params_.cparams.getCommand() << '\n'
222            << "preview " << tostr(params_.cparams.preview()) << '\n';
223 }
224
225
226 void InsetInclude::read(Buffer const *, LyXLex & lex)
227 {
228         params_.cparams.read(lex);
229
230         if (params_.cparams.getCmdName() == "include")
231                 params_.flag = INCLUDE;
232         else if (params_.cparams.getCmdName() == "input")
233                 params_.flag = INPUT;
234         /* FIXME: is this logic necessary now ? */
235         else if (contains(params_.cparams.getCmdName(), "verbatim")) {
236                 params_.flag = VERB;
237                 if (params_.cparams.getCmdName() == "verbatiminput*")
238                         params_.flag = VERBAST;
239         }
240 }
241
242
243 bool InsetInclude::display() const
244 {
245         return !(params_.flag == INPUT);
246 }
247
248
249 string const InsetInclude::getScreenLabel(Buffer const *) const
250 {
251         string temp;
252
253         switch (params_.flag) {
254                 case INPUT: temp += _("Input"); break;
255                 case VERB: temp += _("Verbatim Input"); break;
256                 case VERBAST: temp += _("Verbatim Input*"); break;
257                 case INCLUDE: temp += _("Include"); break;
258         }
259
260         temp += ": ";
261
262         if (params_.cparams.getContents().empty())
263                 temp += "???";
264         else
265                 temp += params_.cparams.getContents();
266
267         return temp;
268 }
269
270
271 string const InsetInclude::getFileName() const
272 {
273         return MakeAbsPath(params_.cparams.getContents(),
274                            OnlyPath(getMasterFilename()));
275 }
276
277
278 string const InsetInclude::getMasterFilename() const
279 {
280         return params_.masterFilename_;
281 }
282
283
284 bool InsetInclude::loadIfNeeded() const
285 {
286         if (isVerbatim())
287                 return false;
288
289         if (!IsLyXFilename(getFileName()))
290                 return false;
291
292         if (bufferlist.exists(getFileName()))
293                 return true;
294
295         // the readonly flag can/will be wrong, not anymore I think.
296         FileInfo finfo(getFileName());
297         if (!finfo.isOK())
298                 return false;
299         return loadLyXFile(bufferlist.newBuffer(getFileName()),
300                            getFileName());
301 }
302
303
304 int InsetInclude::latex(Buffer const * buffer, ostream & os,
305                         LatexRunParams const & runparams) const
306 {
307         string incfile(params_.cparams.getContents());
308
309         // Do nothing if no file name has been specified
310         if (incfile.empty())
311                 return 0;
312
313         if (loadIfNeeded()) {
314                 Buffer * tmp = bufferlist.getBuffer(getFileName());
315
316                 // FIXME: this should be a GUI warning
317                 if (tmp->params.textclass != buffer->params.textclass) {
318                         lyxerr << "WARNING: Included file `"
319                                << MakeDisplayPath(getFileName())
320                                << "' has textclass `"
321                                << tmp->params.getLyXTextClass().name()
322                                << "' while parent file has textclass `"
323                                << buffer->params.getLyXTextClass().name()
324                                << "'." << endl;
325                         //return 0;
326                 }
327
328                 // write it to a file (so far the complete file)
329                 string writefile = ChangeExtension(getFileName(), ".tex");
330
331                 if (!buffer->tmppath.empty() && !runparams.nice) {
332                         incfile = subst(incfile, '/','@');
333 #ifdef __EMX__
334                         incfile = subst(incfile, ':', '$');
335 #endif
336                         writefile = AddName(buffer->tmppath, incfile);
337                 } else
338                         writefile = getFileName();
339                 writefile = ChangeExtension(writefile, ".tex");
340                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
341                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
342
343                 tmp->markDepClean(buffer->tmppath);
344
345                 tmp->makeLaTeXFile(writefile, OnlyPath(getMasterFilename()),
346                                    runparams, true);
347         }
348
349         if (isVerbatim()) {
350                 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
351         } else if (params_.flag == INPUT) {
352                 // \input wants file with extension (default is .tex)
353                 if (!IsLyXFilename(getFileName())) {
354                         os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
355                 } else {
356                         os << '\\' << params_.cparams.getCmdName() << '{'
357                            << ChangeExtension(incfile, ".tex")
358                            <<  '}';
359                 }
360         } else {
361                 // \include don't want extension and demands that the
362                 // file really have .tex
363                 os << '\\' << params_.cparams.getCmdName() << '{'
364                    << ChangeExtension(incfile, string())
365                    << '}';
366         }
367
368         return 0;
369 }
370
371
372 int InsetInclude::ascii(Buffer const *, ostream & os, int) const
373 {
374         if (isVerbatim())
375                 os << GetFileContents(getFileName());
376         return 0;
377 }
378
379
380 int InsetInclude::linuxdoc(Buffer const * buffer, ostream & os) const
381 {
382         string incfile(params_.cparams.getContents());
383
384         // Do nothing if no file name has been specified
385         if (incfile.empty())
386                 return 0;
387
388         if (loadIfNeeded()) {
389                 Buffer * tmp = bufferlist.getBuffer(getFileName());
390
391                 // write it to a file (so far the complete file)
392                 string writefile = ChangeExtension(getFileName(), ".sgml");
393                 if (!buffer->tmppath.empty() && !buffer->niceFile) {
394                         incfile = subst(incfile, '/','@');
395                         writefile = AddName(buffer->tmppath, incfile);
396                 } else
397                         writefile = getFileName();
398
399                 if (IsLyXFilename(getFileName()))
400                         writefile = ChangeExtension(writefile, ".sgml");
401
402                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
403                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
404
405                 tmp->makeLinuxDocFile(writefile, buffer->niceFile, true);
406         }
407
408         if (isVerbatim()) {
409                 os << "<![CDATA["
410                    << GetFileContents(getFileName())
411                    << "]]>";
412         } else
413                 os << '&' << include_label << ';';
414
415         return 0;
416 }
417
418
419 int InsetInclude::docbook(Buffer const * buffer, ostream & os,
420                           bool /*mixcont*/) const
421 {
422         string incfile(params_.cparams.getContents());
423
424         // Do nothing if no file name has been specified
425         if (incfile.empty())
426                 return 0;
427
428         if (loadIfNeeded()) {
429                 Buffer * tmp = bufferlist.getBuffer(getFileName());
430
431                 // write it to a file (so far the complete file)
432                 string writefile = ChangeExtension(getFileName(), ".sgml");
433                 if (!buffer->tmppath.empty() && !buffer->niceFile) {
434                         incfile = subst(incfile, '/','@');
435                         writefile = AddName(buffer->tmppath, incfile);
436                 } else
437                         writefile = getFileName();
438                 if (IsLyXFilename(getFileName()))
439                         writefile = ChangeExtension(writefile, ".sgml");
440
441                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
442                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
443
444                 tmp->makeDocBookFile(writefile, buffer->niceFile, true);
445         }
446
447         if (isVerbatim()) {
448                 os << "<inlinegraphic fileref=\""
449                    << '&' << include_label << ';'
450                    << "\" format=\"linespecific\">";
451         } else
452                 os << '&' << include_label << ';';
453
454         return 0;
455 }
456
457
458 void InsetInclude::validate(LaTeXFeatures & features) const
459 {
460
461         string incfile(params_.cparams.getContents());
462         string writefile;
463
464         Buffer const * const b = bufferlist.getBuffer(getMasterFilename());
465
466         if (b && !b->tmppath.empty() && !b->niceFile && !isVerbatim()) {
467                 incfile = subst(incfile, '/','@');
468                 writefile = AddName(b->tmppath, incfile);
469         } else
470                 writefile = getFileName();
471
472         if (IsLyXFilename(getFileName()))
473                 writefile = ChangeExtension(writefile, ".sgml");
474
475         features.includeFile(include_label, writefile);
476
477         if (isVerbatim())
478                 features.require("verbatim");
479
480         // Here we must do the fun stuff...
481         // Load the file in the include if it needs
482         // to be loaded:
483         if (loadIfNeeded()) {
484                 // a file got loaded
485                 Buffer * const tmp = bufferlist.getBuffer(getFileName());
486                 if (tmp) {
487                         if (b)
488                                 tmp->niceFile = b->niceFile;
489                         tmp->validate(features);
490                 }
491         }
492 }
493
494
495 void InsetInclude::getLabelList(std::vector<string> & list) const
496 {
497         if (loadIfNeeded()) {
498                 Buffer * tmp = bufferlist.getBuffer(getFileName());
499                 tmp->setParentName("");
500                 tmp->getLabelList(list);
501                 tmp->setParentName(getMasterFilename());
502         }
503 }
504
505
506 void InsetInclude::fillWithBibKeys(std::vector<std::pair<string,string> > & keys) const
507 {
508         if (loadIfNeeded()) {
509                 Buffer * tmp = bufferlist.getBuffer(getFileName());
510                 tmp->setParentName("");
511                 tmp->fillWithBibKeys(keys);
512                 tmp->setParentName(getMasterFilename());
513         }
514 }
515
516
517 void InsetInclude::metrics(MetricsInfo & mi, Dimension & dim) const
518 {
519         if (preview_->previewReady()) {
520                 dim.asc = preview_->pimage()->ascent();
521                 dim.des = preview_->pimage()->descent();
522                 dim.wid = preview_->pimage()->width();
523         } else {
524                 if (!set_label_) {
525                         set_label_ = true;
526                         button_.update(getScreenLabel(mi.base.bv->buffer()),
527                                        editable() != NOT_EDITABLE);
528                 }
529                 button_.metrics(mi, dim);
530         }
531         dim_ = dim;
532 }
533
534
535 void InsetInclude::draw(PainterInfo & pi, int x, int y) const
536 {
537         cache(pi.base.bv);
538         if (!preview_->previewReady()) {
539                 button_.draw(pi, x, y);
540                 return;
541         }
542
543         if (!preview_->monitoring())
544                 preview_->startMonitoring();
545
546         pi.pain.image(x, y - dim_.asc, dim_.wid, dim_.height(),
547                             *(preview_->pimage()->image()));
548 }
549
550
551 BufferView * InsetInclude::view() const
552 {
553         return button_.view();
554 }
555
556
557 //
558 // preview stuff
559 //
560
561 void InsetInclude::addPreview(lyx::graphics::PreviewLoader & ploader) const
562 {
563         preview_->addPreview(ploader);
564 }
565
566
567 bool InsetInclude::PreviewImpl::previewWanted() const
568 {
569         return parent().params_.flag == InsetInclude::INPUT &&
570                 parent().params_.cparams.preview() &&
571                 IsFileReadable(parent().getFileName());
572 }
573
574
575 string const InsetInclude::PreviewImpl::latexString() const
576 {
577         if (!view() || !view()->buffer())
578                 return string();
579
580         ostringstream os;
581         LatexRunParams runparams;
582         runparams.flavor = LatexRunParams::LATEX;
583         parent().latex(view()->buffer(), os, runparams);
584
585         return STRCONV(os.str());
586 }
587
588
589 void InsetInclude::PreviewImpl::startMonitoring()
590 {
591         monitor_.reset(new FileMonitor(parent().getFileName(), 2000));
592         monitor_->connect(boost::bind(&PreviewImpl::restartLoading, this));
593         monitor_->start();
594 }
595
596
597 void InsetInclude::PreviewImpl::restartLoading()
598 {
599         lyxerr << "restartLoading()" << std::endl;
600         removePreview();
601         if (view())
602                 view()->updateInset(&parent());
603         generatePreview();
604 }
605
606
607 string const InsetIncludeMailer::name_("include");
608
609 InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
610         : inset_(inset)
611 {}
612
613
614 string const InsetIncludeMailer::inset2string() const
615 {
616         return params2string(inset_.params());
617 }
618
619
620 void InsetIncludeMailer::string2params(string const & in,
621                                        InsetInclude::Params & params)
622 {
623         params = InsetInclude::Params();
624
625         if (in.empty())
626                 return;
627
628         istringstream data(STRCONV(in));
629         LyXLex lex(0,0);
630         lex.setStream(data);
631
632         if (lex.isOK()) {
633                 lex.next();
634                 string const token = lex.getString();
635                 if (token != name_)
636                         return;
637         }
638
639         // This is part of the inset proper that is usually swallowed
640         // by Buffer::readInset
641         if (lex.isOK()) {
642                 lex.next();
643                 string const token = lex.getString();
644                 if (token != "Include")
645                         return;
646         }
647
648         if (lex.isOK()) {
649                 InsetInclude inset(params);
650                 inset.read(0, lex);
651                 params = inset.params();
652         }
653 }
654
655
656 string const
657 InsetIncludeMailer::params2string(InsetInclude::Params const & params)
658 {
659         InsetInclude inset(params);
660         inset.set(params);
661         ostringstream data;
662         data << name_ << ' ';
663         inset.write(0, data);
664         data << "\\end_inset\n";
665         return STRCONV(data.str());
666 }