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