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