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