]> git.lyx.org Git - features.git/blob - src/insets/insetinclude.C
The 'bformat' introduction
[features.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 "bufferlist.h"
16 #include "BufferView.h"
17 #include "debug.h"
18 #include "funcrequest.h"
19 #include "gettext.h"
20 #include "LaTeXFeatures.h"
21 #include "lyxlex.h"
22 #include "lyxrc.h"
23 #include "Lsstream.h"
24
25 #include "frontends/Dialogs.h"
26 #include "frontends/LyXView.h"
27 #include "frontends/Painter.h"
28
29 #include "support/filetools.h"
30 #include "support/FileInfo.h"
31 #include "support/FileMonitor.h"
32 #include "support/lstrings.h"
33
34 #include "graphics/PreviewedInset.h"
35 #include "graphics/PreviewImage.h"
36
37 #include <boost/bind.hpp>
38
39 #include <cstdlib>
40
41
42 using std::ostream;
43 using std::endl;
44 using std::vector;
45 using std::pair;
46
47 extern BufferList bufferlist;
48
49
50 class InsetInclude::PreviewImpl : public grfx::PreviewedInset {
51 public:
52         ///
53         PreviewImpl(InsetInclude & p) : PreviewedInset(p) {}
54
55         ///
56         bool previewWanted() const;
57         ///
58         string const latexString() const;
59         ///
60         InsetInclude & parent() const {
61                 return *static_cast<InsetInclude*>(inset());
62         }
63
64         ///
65         bool monitoring() const { return monitor_.get(); }
66         ///
67         void startMonitoring();
68         ///
69         void stopMonitoring() { monitor_.reset(); }
70
71 private:
72         /// Invoked by monitor_ should the parent file change.
73         void restartLoading();
74         ///
75         boost::scoped_ptr<FileMonitor> monitor_;
76 };
77
78
79 namespace {
80
81 string const uniqueID()
82 {
83         static unsigned int seed = 1000;
84
85         ostringstream ost;
86         ost << "file" << ++seed;
87
88         // Needed if we use lyxstring.
89         return STRCONV(ost.str());
90 }
91
92 } // namespace anon
93
94
95 InsetInclude::InsetInclude(Params const & p)
96         : params_(p), include_label(uniqueID()),
97           preview_(new PreviewImpl(*this))
98 {}
99
100
101 InsetInclude::InsetInclude(InsetCommandParams const & p, Buffer const & b)
102         : include_label(uniqueID()),
103           preview_(new PreviewImpl(*this))
104 {
105         params_.cparams = p;
106         params_.masterFilename_ = b.fileName();
107 }
108
109
110 InsetInclude::~InsetInclude()
111 {
112         InsetIncludeMailer mailer(*this);
113         mailer.hideDialog();
114 }
115
116
117 dispatch_result InsetInclude::localDispatch(FuncRequest const & cmd)
118 {
119         dispatch_result result = UNDISPATCHED;
120
121         switch (cmd.action) {
122         case LFUN_INSET_MODIFY: {
123                 InsetInclude::Params p;
124                 InsetIncludeMailer::string2params(cmd.argument, p);
125                 if (p.cparams.getCmdName().empty())
126                         break;
127
128                 set(p);
129                 params_.masterFilename_ = cmd.view()->buffer()->fileName();
130
131                 cmd.view()->updateInset(this);
132                 result = DISPATCHED;
133         }
134         break;
135
136         case LFUN_INSET_DIALOG_UPDATE: {
137                 InsetIncludeMailer mailer(*this);
138                 mailer.updateDialog(cmd.view());
139         }
140         break;
141
142         case LFUN_MOUSE_RELEASE:
143                 edit(cmd.view(), cmd.x, cmd.y, cmd.button());
144                 break;
145
146         default:
147                 break;
148         }
149
150         return result;
151 }
152
153
154 InsetInclude::Params const & InsetInclude::params() const
155 {
156         return params_;
157 }
158
159
160 bool InsetInclude::Params::operator==(Params const & o) const
161 {
162         if (cparams == o.cparams && flag == o.flag &&
163             masterFilename_ == o.masterFilename_)
164                 return true;
165
166         return false;
167 }
168
169
170 bool InsetInclude::Params::operator!=(Params const & o) const
171 {
172         return !(*this == o);
173 }
174
175
176 void InsetInclude::set(Params const & p)
177 {
178         params_ = p;
179
180         string command;
181
182         switch (params_.flag) {
183                 case INCLUDE:
184                         command="include";
185                         break;
186                 case VERB:
187                         command="verbatiminput";
188                         break;
189                 case INPUT:
190                         command="input";
191                         break;
192                 case VERBAST:
193                         command="verbatiminput*";
194                         break;
195         }
196
197         params_.cparams.setCmdName(command);
198
199         if (preview_->monitoring())
200                 preview_->stopMonitoring();
201
202         if (grfx::PreviewedInset::activated() && params_.flag == INPUT)
203                 preview_->generatePreview();
204 }
205
206
207 Inset * InsetInclude::clone(Buffer const & buffer, bool) const
208 {
209         Params p(params_);
210         p.masterFilename_ = buffer.fileName();
211
212         return new InsetInclude(p);
213 }
214
215
216 void InsetInclude::edit(BufferView * bv, int, int, mouse_button::state)
217 {
218         InsetIncludeMailer mailer(*this);
219         mailer.showDialog(bv);
220 }
221
222
223 void InsetInclude::edit(BufferView * bv, bool)
224 {
225         edit(bv, 0, 0, mouse_button::none);
226 }
227
228
229 void InsetInclude::write(Buffer const *, 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         params_.cparams.read(lex);
239
240         if (params_.cparams.getCmdName() == "include")
241                 params_.flag = INCLUDE;
242         else if (params_.cparams.getCmdName() == "input")
243                 params_.flag = INPUT;
244         /* FIXME: is this logic necessary now ? */
245         else if (contains(params_.cparams.getCmdName(), "verbatim")) {
246                 params_.flag = VERB;
247                 if (params_.cparams.getCmdName() == "verbatiminput*")
248                         params_.flag = VERBAST;
249         }
250 }
251
252
253 bool InsetInclude::display() const
254 {
255         return !(params_.flag == INPUT);
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
310         return bufferlist.loadLyXFile(getFileName(), false) != 0;
311 }
312
313
314 int InsetInclude::latex(Buffer const * buffer, ostream & os,
315                         bool /*fragile*/, bool /*fs*/) 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->tmppath.empty()
342                     && !buffer->niceFile) {
343                         incfile = subst(incfile, '/','@');
344 #ifdef __EMX__
345                         incfile = subst(incfile, ':', '$');
346 #endif
347                         writefile = AddName(buffer->tmppath, incfile);
348                 } else
349                         writefile = getFileName();
350                 writefile = ChangeExtension(writefile, ".tex");
351                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
352                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
353
354                 tmp->markDepClean(buffer->tmppath);
355
356                 tmp->makeLaTeXFile(writefile,
357                                    OnlyPath(getMasterFilename()),
358                                    buffer->niceFile, true);
359         }
360
361         if (isVerbatim()) {
362                 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
363         } else if (params_.flag == INPUT) {
364                 // \input wants file with extension (default is .tex)
365                 if (!IsLyXFilename(getFileName())) {
366                         os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
367                 } else {
368                         os << '\\' << params_.cparams.getCmdName() << '{'
369                            << ChangeExtension(incfile, ".tex")
370                            <<  '}';
371                 }
372         } else {
373                 // \include don't want extension and demands that the
374                 // file really have .tex
375                 os << '\\' << params_.cparams.getCmdName() << '{'
376                    << ChangeExtension(incfile, string())
377                    << '}';
378         }
379
380         return 0;
381 }
382
383
384 int InsetInclude::ascii(Buffer const *, ostream & os, int) const
385 {
386         if (isVerbatim())
387                 os << GetFileContents(getFileName());
388         return 0;
389 }
390
391
392 int InsetInclude::linuxdoc(Buffer const * buffer, ostream & os) const
393 {
394         string incfile(params_.cparams.getContents());
395
396         // Do nothing if no file name has been specified
397         if (incfile.empty())
398                 return 0;
399
400         if (loadIfNeeded()) {
401                 Buffer * tmp = bufferlist.getBuffer(getFileName());
402
403                 // write it to a file (so far the complete file)
404                 string writefile = ChangeExtension(getFileName(), ".sgml");
405                 if (!buffer->tmppath.empty() && !buffer->niceFile) {
406                         incfile = subst(incfile, '/','@');
407                         writefile = AddName(buffer->tmppath, incfile);
408                 } else
409                         writefile = getFileName();
410
411                 if (IsLyXFilename(getFileName()))
412                         writefile = ChangeExtension(writefile, ".sgml");
413
414                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
415                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
416
417                 tmp->makeLinuxDocFile(writefile, buffer->niceFile, true);
418         }
419
420         if (isVerbatim()) {
421                 os << "<![CDATA["
422                    << GetFileContents(getFileName())
423                    << "]]>";
424         } else
425                 os << '&' << include_label << ';';
426
427         return 0;
428 }
429
430
431 int InsetInclude::docbook(Buffer const * buffer, ostream & os,
432                           bool /*mixcont*/) const
433 {
434         string incfile(params_.cparams.getContents());
435
436         // Do nothing if no file name has been specified
437         if (incfile.empty())
438                 return 0;
439
440         if (loadIfNeeded()) {
441                 Buffer * tmp = bufferlist.getBuffer(getFileName());
442
443                 // write it to a file (so far the complete file)
444                 string writefile = ChangeExtension(getFileName(), ".sgml");
445                 if (!buffer->tmppath.empty() && !buffer->niceFile) {
446                         incfile = subst(incfile, '/','@');
447                         writefile = AddName(buffer->tmppath, incfile);
448                 } else
449                         writefile = getFileName();
450                 if (IsLyXFilename(getFileName()))
451                         writefile = ChangeExtension(writefile, ".sgml");
452
453                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
454                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
455
456                 tmp->makeDocBookFile(writefile, buffer->niceFile, true);
457         }
458
459         if (isVerbatim()) {
460                 os << "<inlinegraphic fileref=\""
461                    << '&' << include_label << ';'
462                    << "\" format=\"linespecific\">";
463         } else
464                 os << '&' << include_label << ';';
465
466         return 0;
467 }
468
469
470 void InsetInclude::validate(LaTeXFeatures & features) const
471 {
472
473         string incfile(params_.cparams.getContents());
474         string writefile;
475
476         Buffer const * const b = bufferlist.getBuffer(getMasterFilename());
477
478         if (b && !b->tmppath.empty() && !b->niceFile && !isVerbatim()) {
479                 incfile = subst(incfile, '/','@');
480                 writefile = AddName(b->tmppath, incfile);
481         } else
482                 writefile = getFileName();
483
484         if (IsLyXFilename(getFileName()))
485                 writefile = ChangeExtension(writefile, ".sgml");
486
487         features.includeFile(include_label, writefile);
488
489         if (isVerbatim())
490                 features.require("verbatim");
491
492         // Here we must do the fun stuff...
493         // Load the file in the include if it needs
494         // to be loaded:
495         if (loadIfNeeded()) {
496                 // a file got loaded
497                 Buffer * const tmp = bufferlist.getBuffer(getFileName());
498                 if (tmp) {
499                         if (b)
500                                 tmp->niceFile = b->niceFile;
501                         tmp->validate(features);
502                 }
503         }
504 }
505
506
507 vector<string> const InsetInclude::getLabelList() const
508 {
509         vector<string> l;
510
511         if (loadIfNeeded()) {
512                 Buffer * tmp = bufferlist.getBuffer(getFileName());
513                 tmp->setParentName("");
514                 l = tmp->getLabelList();
515                 tmp->setParentName(getMasterFilename());
516         }
517
518         return l;
519 }
520
521
522 void InsetInclude::fillWithBibKeys(vector<pair<string,string> > & keys) const
523 {
524         if (loadIfNeeded()) {
525                 Buffer * tmp = bufferlist.getBuffer(getFileName());
526                 tmp->setParentName("");
527                 tmp->fillWithBibKeys(keys);
528                 tmp->setParentName(getMasterFilename());
529         }
530 }
531
532
533 int InsetInclude::ascent(BufferView * bv, LyXFont const & font) const
534 {
535         return preview_->previewReady() ?
536                 preview_->pimage()->ascent() : InsetButton::ascent(bv, font);
537 }
538
539
540 int InsetInclude::descent(BufferView * bv, LyXFont const & font) const
541 {
542         return preview_->previewReady() ?
543                 preview_->pimage()->descent() : InsetButton::descent(bv, font);
544 }
545
546
547 int InsetInclude::width(BufferView * bv, LyXFont const & font) const
548 {
549         return preview_->previewReady() ?
550                 preview_->pimage()->width() : InsetButton::width(bv, font);
551 }
552
553
554 void InsetInclude::draw(BufferView * bv, LyXFont const & font, int y,
555                         float & xx) const
556 {
557         cache(bv);
558         if (!preview_->previewReady()) {
559                 InsetButton::draw(bv, font, y, xx);
560                 return;
561         }
562
563         if (!preview_->monitoring())
564                 preview_->startMonitoring();
565
566         int const x = int(xx);
567         int const w = width(bv, font);
568         int const d = descent(bv, font);
569         int const a = ascent(bv, font);
570         int const h = a + d;
571
572         bv->painter().image(x, y - a, w, h,
573                             *(preview_->pimage()->image()));
574
575         xx += w;
576 }
577
578
579 //
580 // preview stuff
581 //
582
583 void InsetInclude::addPreview(grfx::PreviewLoader & ploader) const
584 {
585         preview_->addPreview(ploader);
586 }
587
588
589 bool InsetInclude::PreviewImpl::previewWanted() const
590 {
591         return parent().params_.flag == InsetInclude::INPUT &&
592                 parent().params_.cparams.preview() &&
593                 IsFileReadable(parent().getFileName());
594 }
595
596
597 string const InsetInclude::PreviewImpl::latexString() const
598 {
599         if (!view() || !view()->buffer())
600                 return string();
601
602         ostringstream os;
603         parent().latex(view()->buffer(), os, false, false);
604
605         return STRCONV(os.str());
606 }
607
608
609 void InsetInclude::PreviewImpl::startMonitoring()
610 {
611         monitor_.reset(new FileMonitor(parent().getFileName(), 2000));
612         monitor_->connect(boost::bind(&PreviewImpl::restartLoading, this));
613         monitor_->start();
614 }
615
616
617 void InsetInclude::PreviewImpl::restartLoading()
618 {
619         lyxerr << "restartLoading()" << std::endl;
620         removePreview();
621         if (view())
622                 view()->updateInset(&parent());
623         generatePreview();
624 }
625
626
627 string const InsetIncludeMailer::name_("include");
628
629 InsetIncludeMailer::InsetIncludeMailer(InsetInclude & inset)
630         : inset_(inset)
631 {}
632
633
634 string const InsetIncludeMailer::inset2string() const
635 {
636         return params2string(inset_.params());
637 }
638
639
640 void InsetIncludeMailer::string2params(string const & in,
641                                        InsetInclude::Params & params)
642 {
643         params = InsetInclude::Params();
644
645         if (in.empty())
646                 return;
647         
648         istringstream data(in);
649         LyXLex lex(0,0);
650         lex.setStream(data);
651
652         if (lex.isOK()) {
653                 lex.next();
654                 string const token = lex.getString();
655                 if (token != name_)
656                         return;
657         }
658
659         // This is part of the inset proper that is usually swallowed
660         // by Buffer::readInset
661         if (lex.isOK()) {
662                 lex.next();
663                 string const token = lex.getString();
664                 if (token != "Include")
665                         return;
666         }
667
668         if (lex.isOK()) {
669                 InsetInclude inset(params);
670                 inset.read(0, lex);
671                 params = inset.params();
672         }
673 }
674
675
676 string const
677 InsetIncludeMailer::params2string(InsetInclude::Params const & params)
678 {
679         InsetInclude inset(params);
680         inset.set(params);
681         ostringstream data;
682         data << name_ << ' ';
683         inset.write(0, data);
684         data << "\\end_inset\n";
685
686         return data.str();
687 }