]> git.lyx.org Git - lyx.git/blob - src/insets/insetinclude.C
unbork insetinclude
[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 #include <config.h>
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #endif
15
16 #include "insetinclude.h"
17 #include "buffer.h"
18 #include "bufferlist.h"
19 #include "BufferView.h"
20 #include "debug.h"
21 #include "lyxrc.h"
22 #include "LaTeXFeatures.h"
23 #include "gettext.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         hideDialog();
113 }
114
115
116 InsetInclude::Params const & InsetInclude::params() const
117 {
118         return params_;
119 }
120
121
122 bool InsetInclude::Params::operator==(Params const & o) const
123 {
124         if (cparams == o.cparams && flag == o.flag &&
125             masterFilename_ == o.masterFilename_)
126                 return true;
127
128         return false;
129 }
130
131
132 bool InsetInclude::Params::operator!=(Params const & o) const
133 {
134         return !(*this == o);
135 }
136
137
138 void InsetInclude::set(Params const & p)
139 {
140         params_ = p;
141
142         string command;
143  
144         switch (params_.flag) {
145                 case INCLUDE:
146                         command="include";
147                         break;
148                 case VERB:
149                         command="verbatiminput";
150                         break;
151                 case INPUT:
152                         command="input";
153                         break;
154                 case VERBAST:
155                         command="verbatiminput*";
156                         break;
157         }
158  
159         params_.cparams.setCmdName(command); 
160  
161         if (preview_->monitoring())
162                 preview_->stopMonitoring();
163
164         if (grfx::PreviewedInset::activated() && params_.flag == INPUT)
165                 preview_->generatePreview();
166 }
167
168
169 Inset * InsetInclude::clone(Buffer const & buffer, bool) const
170 {
171         Params p(params_);
172         p.masterFilename_ = buffer.fileName();
173
174         return new InsetInclude(p);
175 }
176
177
178 void InsetInclude::edit(BufferView * bv, int, int, mouse_button::state)
179 {
180         bv->owner()->getDialogs().showInclude(this);
181 }
182
183
184 void InsetInclude::edit(BufferView * bv, bool)
185 {
186         edit(bv, 0, 0, mouse_button::none);
187 }
188
189
190 void InsetInclude::write(Buffer const *, ostream & os) const
191 {
192         os << "Include " << params_.cparams.getCommand() << '\n'
193            << "preview " << tostr(params_.cparams.preview()) << '\n';
194 }
195
196
197 void InsetInclude::read(Buffer const *, LyXLex & lex)
198 {
199         params_.cparams.read(lex);
200
201         if (params_.cparams.getCmdName() == "include")
202                 params_.flag = INCLUDE;
203         else if (params_.cparams.getCmdName() == "input")
204                 params_.flag = INPUT;
205         /* FIXME: is this logic necessary now ? */
206         else if (contains(params_.cparams.getCmdName(), "verbatim")) {
207                 params_.flag = VERB;
208                 if (params_.cparams.getCmdName() == "verbatiminput*")
209                         params_.flag = VERBAST;
210         }
211 }
212
213
214 bool InsetInclude::display() const
215 {
216         return !(params_.flag == INPUT);
217 }
218
219
220 string const InsetInclude::getScreenLabel(Buffer const *) const
221 {
222         string temp;
223
224         switch (params_.flag) {
225                 case INPUT: temp += _("Input"); break;
226                 case VERB: temp += _("Verbatim Input"); break;
227                 case VERBAST: temp += _("Verbatim Input*"); break;
228                 case INCLUDE: temp += _("Include"); break;
229         }
230
231         temp += ": ";
232
233         if (params_.cparams.getContents().empty())
234                 temp += "???";
235         else
236                 temp += params_.cparams.getContents();
237
238         return temp;
239 }
240
241
242 string const InsetInclude::getRelFileBaseName() const
243 {
244         return OnlyFilename(ChangeExtension(params_.cparams.getContents(), string()));
245 }
246
247
248 string const InsetInclude::getFileName() const
249 {
250         return MakeAbsPath(params_.cparams.getContents(),
251                            OnlyPath(getMasterFilename()));
252 }
253
254
255 string const InsetInclude::getMasterFilename() const
256 {
257         return params_.masterFilename_;
258 }
259
260
261 bool InsetInclude::loadIfNeeded() const
262 {
263         if (isVerbatim())
264                 return false;
265
266         if (!IsLyXFilename(getFileName()))
267                 return false;
268
269         if (bufferlist.exists(getFileName()))
270                 return true;
271
272         // the readonly flag can/will be wrong, not anymore I think.
273         FileInfo finfo(getFileName());
274         if (!finfo.isOK())
275                 return false;
276
277         return bufferlist.readFile(getFileName(), !finfo.writable()) != 0;
278 }
279
280
281 int InsetInclude::latex(Buffer const * buffer, ostream & os,
282                         bool /*fragile*/, bool /*fs*/) const
283 {
284         string incfile(params_.cparams.getContents());
285
286         // Do nothing if no file name has been specified
287         if (incfile.empty())
288                 return 0;
289
290         if (loadIfNeeded()) {
291                 Buffer * tmp = bufferlist.getBuffer(getFileName());
292
293                 // FIXME: this should be a GUI warning
294                 if (tmp->params.textclass != buffer->params.textclass) {
295                         lyxerr << "WARNING: Included file `"
296                                << MakeDisplayPath(getFileName())
297                                << "' has textclass `"
298                                << tmp->params.getLyXTextClass().name()
299                                << "' while parent file has textclass `"
300                                << buffer->params.getLyXTextClass().name()
301                                << "'." << endl;
302                         //return 0;
303                 }
304
305                 // write it to a file (so far the complete file)
306                 string writefile = ChangeExtension(getFileName(), ".tex");
307
308                 if (!buffer->tmppath.empty()
309                     && !buffer->niceFile) {
310                         incfile = subst(incfile, '/','@');
311 #ifdef __EMX__
312                         incfile = subst(incfile, ':', '$');
313 #endif
314                         writefile = AddName(buffer->tmppath, incfile);
315                 } else
316                         writefile = getFileName();
317                 writefile = ChangeExtension(writefile, ".tex");
318                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
319                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
320
321                 tmp->markDepClean(buffer->tmppath);
322
323                 tmp->makeLaTeXFile(writefile,
324                                    OnlyPath(getMasterFilename()),
325                                    buffer->niceFile, true);
326         }
327
328         if (isVerbatim()) {
329                 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
330         } else if (params_.flag == INPUT) {
331                 // \input wants file with extension (default is .tex)
332                 if (!IsLyXFilename(getFileName())) {
333                         os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
334                 } else {
335                         os << '\\' << params_.cparams.getCmdName() << '{'
336                            << ChangeExtension(incfile, ".tex")
337                            <<  '}';
338                 }
339         } else {
340                 // \include don't want extension and demands that the
341                 // file really have .tex
342                 os << '\\' << params_.cparams.getCmdName() << '{'
343                    << ChangeExtension(incfile, string())
344                    << '}';
345         }
346
347         return 0;
348 }
349
350
351 int InsetInclude::ascii(Buffer const *, ostream & os, int) const
352 {
353         if (isVerbatim())
354                 os << GetFileContents(getFileName());
355         return 0;
356 }
357
358
359 int InsetInclude::linuxdoc(Buffer const * buffer, ostream & os) const
360 {
361         string incfile(params_.cparams.getContents());
362
363         // Do nothing if no file name has been specified
364         if (incfile.empty())
365                 return 0;
366
367         if (loadIfNeeded()) {
368                 Buffer * tmp = bufferlist.getBuffer(getFileName());
369
370                 // write it to a file (so far the complete file)
371                 string writefile = ChangeExtension(getFileName(), ".sgml");
372                 if (!buffer->tmppath.empty() && !buffer->niceFile) {
373                         incfile = subst(incfile, '/','@');
374                         writefile = AddName(buffer->tmppath, incfile);
375                 } else
376                         writefile = getFileName();
377
378                 if (IsLyXFilename(getFileName()))
379                         writefile = ChangeExtension(writefile, ".sgml");
380
381                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
382                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
383
384                 tmp->makeLinuxDocFile(writefile, buffer->niceFile, true);
385         }
386
387         if (isVerbatim()) {
388                 os << "<![CDATA["
389                    << GetFileContents(getFileName())
390                    << "]]>";
391         } else
392                 os << '&' << include_label << ';';
393
394         return 0;
395 }
396
397
398 int InsetInclude::docbook(Buffer const * buffer, ostream & os,
399                           bool /*mixcont*/) const
400 {
401         string incfile(params_.cparams.getContents());
402
403         // Do nothing if no file name has been specified
404         if (incfile.empty())
405                 return 0;
406
407         if (loadIfNeeded()) {
408                 Buffer * tmp = bufferlist.getBuffer(getFileName());
409
410                 // write it to a file (so far the complete file)
411                 string writefile = ChangeExtension(getFileName(), ".sgml");
412                 if (!buffer->tmppath.empty() && !buffer->niceFile) {
413                         incfile = subst(incfile, '/','@');
414                         writefile = AddName(buffer->tmppath, incfile);
415                 } else
416                         writefile = getFileName();
417                 if (IsLyXFilename(getFileName()))
418                         writefile = ChangeExtension(writefile, ".sgml");
419
420                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
421                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
422
423                 tmp->makeDocBookFile(writefile, buffer->niceFile, true);
424         }
425
426         if (isVerbatim()) {
427                 os << "<inlinegraphic fileref=\""
428                    << '&' << include_label << ';'
429                    << "\" format=\"linespecific\">";
430         } else
431                 os << '&' << include_label << ';';
432
433         return 0;
434 }
435
436
437 void InsetInclude::validate(LaTeXFeatures & features) const
438 {
439
440         string incfile(params_.cparams.getContents());
441         string writefile;
442
443         Buffer const * const b = bufferlist.getBuffer(getMasterFilename());
444
445         if (b && !b->tmppath.empty() && !b->niceFile && !isVerbatim()) {
446                 incfile = subst(incfile, '/','@');
447                 writefile = AddName(b->tmppath, incfile);
448         } else
449                 writefile = getFileName();
450
451         if (IsLyXFilename(getFileName()))
452                 writefile = ChangeExtension(writefile, ".sgml");
453
454         features.includeFile(include_label, writefile);
455
456         if (isVerbatim())
457                 features.require("verbatim");
458
459         // Here we must do the fun stuff...
460         // Load the file in the include if it needs
461         // to be loaded:
462         if (loadIfNeeded()) {
463                 // a file got loaded
464                 Buffer * const tmp = bufferlist.getBuffer(getFileName());
465                 if (tmp) {
466                         if (b)
467                                 tmp->niceFile = b->niceFile;
468                         tmp->validate(features);
469                 }
470         }
471 }
472
473
474 vector<string> const InsetInclude::getLabelList() const
475 {
476         vector<string> l;
477
478         if (loadIfNeeded()) {
479                 Buffer * tmp = bufferlist.getBuffer(getFileName());
480                 tmp->setParentName("");
481                 l = tmp->getLabelList();
482                 tmp->setParentName(getMasterFilename());
483         }
484
485         return l;
486 }
487
488
489 vector<pair<string,string> > const InsetInclude::getKeys() const
490 {
491         vector<pair<string,string> > keys;
492
493         if (loadIfNeeded()) {
494                 Buffer * tmp = bufferlist.getBuffer(getFileName());
495                 tmp->setParentName("");
496                 keys = tmp->getBibkeyList();
497                 tmp->setParentName(getMasterFilename());
498         }
499
500         return keys;
501 }
502
503
504 int InsetInclude::ascent(BufferView * bv, LyXFont const & font) const
505 {
506         return preview_->previewReady() ?
507                 preview_->pimage()->ascent() : InsetButton::ascent(bv, font);
508 }
509
510
511 int InsetInclude::descent(BufferView * bv, LyXFont const & font) const
512 {
513         return preview_->previewReady() ?
514                 preview_->pimage()->descent() : InsetButton::descent(bv, font);
515 }
516
517
518 int InsetInclude::width(BufferView * bv, LyXFont const & font) const
519 {
520         return preview_->previewReady() ?
521                 preview_->pimage()->width() : InsetButton::width(bv, font);
522 }
523
524
525 void InsetInclude::draw(BufferView * bv, LyXFont const & font, int y,
526                         float & xx, bool b) const
527 {
528         preview_->setView(bv);
529         if (!preview_->previewReady()) {
530                 InsetButton::draw(bv, font, y, xx, b);
531                 return;
532         }
533
534         if (!preview_->monitoring())
535                 preview_->startMonitoring();
536
537         int const x = int(xx);
538         int const w = width(bv, font);
539         int const d = descent(bv, font);
540         int const a = ascent(bv, font);
541         int const h = a + d;
542
543         bv->painter().image(x, y - a, w, h,
544                             *(preview_->pimage()->image(*this, *bv)));
545
546         xx += w;
547 }
548
549
550 //
551 // preview stuff
552 //
553
554 void InsetInclude::addPreview(grfx::PreviewLoader & ploader) const
555 {
556         preview_->addPreview(ploader);
557 }
558
559
560 bool InsetInclude::PreviewImpl::previewWanted() const
561 {
562         return parent().params_.flag == InsetInclude::INPUT &&
563                 parent().params_.cparams.preview() &&
564                 IsFileReadable(parent().getFileName());
565 }
566
567
568 string const InsetInclude::PreviewImpl::latexString() const
569 {
570         if (!view() || !view()->buffer())
571                 return string();
572
573         ostringstream os;
574         parent().latex(view()->buffer(), os, false, false);
575
576         return STRCONV(os.str());
577 }
578
579
580 void InsetInclude::PreviewImpl::startMonitoring()
581 {
582         monitor_.reset(new FileMonitor(parent().getFileName(), 2000));
583         monitor_->connect(boost::bind(&PreviewImpl::restartLoading, this));
584         monitor_->start();
585 }
586
587
588 void InsetInclude::PreviewImpl::restartLoading()
589 {
590         lyxerr << "restartLoading()" << std::endl;
591         removePreview();
592         if (view())
593                 view()->updateInset(&parent(), false);
594         generatePreview();
595 }