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