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