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