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