]> git.lyx.org Git - lyx.git/blob - src/insets/insetinclude.C
Change the LyX file format, adding a "preview" flag to Include insets.
[lyx.git] / src / insets / insetinclude.C
1 #include <config.h>
2
3 #ifdef __GNUG__
4 #pragma implementation
5 #endif
6
7 #include "insetinclude.h"
8 #include "buffer.h"
9 #include "bufferlist.h"
10 #include "BufferView.h"
11 #include "debug.h"
12 #include "lyxrc.h"
13 #include "LaTeXFeatures.h"
14 #include "gettext.h"
15
16 #include "frontends/Dialogs.h"
17 #include "frontends/LyXView.h"
18 #include "frontends/Painter.h"
19
20 #include "support/filetools.h"
21 #include "support/FileInfo.h"
22 #include "support/lstrings.h"
23
24 #include "graphics/PreviewedInset.h"
25 #include "graphics/PreviewImage.h"
26
27 #include <cstdlib>
28
29
30 using std::ostream;
31 using std::endl;
32 using std::vector;
33 using std::pair;
34
35 extern BufferList bufferlist;
36
37
38 class InsetInclude::PreviewImpl : public grfx::PreviewedInset {
39 public:
40         ///
41         PreviewImpl(InsetInclude & p) : PreviewedInset(p) {}
42
43         ///
44         bool previewWanted() const;
45         ///
46         string const latexString() const;
47         ///
48         InsetInclude & parent() const {
49                 return *static_cast<InsetInclude*>(inset());
50         }
51 };
52
53
54 namespace {
55
56 string const uniqueID()
57 {
58         static unsigned int seed = 1000;
59
60         ostringstream ost;
61         ost << "file" << ++seed;
62
63         // Needed if we use lyxstring.
64         return ost.str().c_str();
65 }
66
67 } // namespace anon
68
69
70 InsetInclude::InsetInclude(Params const & p)
71         : params_(p), include_label(uniqueID()),
72           preview_(new PreviewImpl(*this))
73 {}
74
75
76 InsetInclude::InsetInclude(InsetCommandParams const & p, Buffer const & b)
77         : include_label(uniqueID()),
78           preview_(new PreviewImpl(*this))
79 {
80         params_.cparams = p;
81         params_.masterFilename_ = b.fileName();
82 }
83
84
85 InsetInclude::~InsetInclude()
86 {
87         hideDialog();
88 }
89
90
91 InsetInclude::Params const & InsetInclude::params() const
92 {
93         return params_;
94 }
95
96
97 bool InsetInclude::Params::operator==(Params const & o) const
98 {
99         if (cparams == o.cparams && flag == o.flag &&
100             noload == o.noload && masterFilename_ == o.masterFilename_)
101                 return true;
102
103         return false;
104 }
105
106
107 bool InsetInclude::Params::operator!=(Params const & o) const
108 {
109         return !(*this == o);
110 }
111
112
113 void InsetInclude::set(Params const & p)
114 {
115         params_ = p;
116
117         // Just to be safe...
118         string command;
119
120         switch (params_.flag) {
121                 case INCLUDE:
122                         command="include";
123                         break;
124                 case VERB:
125                         command="verbatiminput";
126                         break;
127                 case INPUT:
128                         command="input";
129                         break;
130                 case VERBAST:
131                         command="verbatiminput*";
132                         break;
133         }
134
135         params_.cparams.setCmdName(command);
136
137         if (grfx::PreviewedInset::activated() && params_.flag == INPUT)
138                 preview_->generatePreview();
139 }
140
141
142 Inset * InsetInclude::clone(Buffer const & buffer, bool) const
143 {
144         Params p(params_);
145         p.masterFilename_ = buffer.fileName();
146
147         return new InsetInclude(p);
148 }
149
150
151 void InsetInclude::edit(BufferView * bv, int, int, mouse_button::state)
152 {
153         bv->owner()->getDialogs()->showInclude(this);
154 }
155
156
157 void InsetInclude::edit(BufferView * bv, bool)
158 {
159         edit(bv, 0, 0, mouse_button::none);
160 }
161
162
163 void InsetInclude::write(Buffer const *, ostream & os) const
164 {
165         os << "Include " << params_.cparams.getCommand() << '\n'
166            << "preview " << tostr(params_.cparams.preview()) << '\n';
167 }
168
169
170 void InsetInclude::read(Buffer const *, LyXLex & lex)
171 {
172         params_.cparams.read(lex);
173
174         if (params_.cparams.getCmdName() == "include")
175                 params_.flag = INCLUDE;
176         else if (params_.cparams.getCmdName() == "input")
177                 params_.flag = INPUT;
178         /* FIXME: is this logic necessary now ? */
179         else if (contains(params_.cparams.getCmdName(), "verbatim")) {
180                 params_.flag = VERB;
181                 if (params_.cparams.getCmdName() == "verbatiminput*")
182                         params_.flag = VERBAST;
183         }
184 }
185
186
187 bool InsetInclude::display() const
188 {
189         return !(params_.flag == INPUT);
190 }
191
192
193 string const InsetInclude::getScreenLabel(Buffer const *) const
194 {
195         string temp;
196
197         switch (params_.flag) {
198                 case INPUT: temp += _("Input"); break;
199                 case VERB: temp += _("Verbatim Input"); break;
200                 case VERBAST: temp += _("Verbatim Input*"); break;
201                 case INCLUDE: temp += _("Include"); break;
202         }
203
204         temp += ": ";
205
206         if (params_.cparams.getContents().empty())
207                 temp += "???";
208         else
209                 temp += params_.cparams.getContents();
210
211         return temp;
212 }
213
214
215 string const InsetInclude::getRelFileBaseName() const
216 {
217         return OnlyFilename(ChangeExtension(params_.cparams.getContents(), string()));
218 }
219
220
221 string const InsetInclude::getFileName() const
222 {
223         return MakeAbsPath(params_.cparams.getContents(),
224                            OnlyPath(getMasterFilename()));
225 }
226
227
228 string const InsetInclude::getMasterFilename() const
229 {
230         return params_.masterFilename_;
231 }
232
233
234 bool InsetInclude::loadIfNeeded() const
235 {
236         if (params_.noload || isVerbatim())
237                 return false;
238
239         if (!IsLyXFilename(getFileName()))
240                 return false;
241
242         if (bufferlist.exists(getFileName()))
243                 return true;
244
245         // the readonly flag can/will be wrong, not anymore I think.
246         FileInfo finfo(getFileName());
247         if (!finfo.isOK())
248                 return false;
249
250         return bufferlist.readFile(getFileName(), !finfo.writable()) != 0;
251 }
252
253
254 int InsetInclude::latex(Buffer const * buffer, ostream & os,
255                         bool /*fragile*/, bool /*fs*/) const
256 {
257         string incfile(params_.cparams.getContents());
258
259         // Do nothing if no file name has been specified
260         if (incfile.empty())
261                 return 0;
262
263         if (loadIfNeeded()) {
264                 Buffer * tmp = bufferlist.getBuffer(getFileName());
265
266                 // FIXME: this should be a GUI warning
267                 if (tmp->params.textclass != buffer->params.textclass) {
268                         lyxerr << "WARNING: Included file `"
269                                << MakeDisplayPath(getFileName())
270                                << "' has textclass `"
271                                << tmp->params.getLyXTextClass().name()
272                                << "' while parent file has textclass `"
273                                << buffer->params.getLyXTextClass().name()
274                                << "'." << endl;
275                         //return 0;
276                 }
277
278                 // write it to a file (so far the complete file)
279                 string writefile = ChangeExtension(getFileName(), ".tex");
280
281                 if (!buffer->tmppath.empty()
282                     && !buffer->niceFile) {
283                         incfile = subst(incfile, '/','@');
284 #ifdef __EMX__
285                         incfile = subst(incfile, ':', '$');
286 #endif
287                         writefile = AddName(buffer->tmppath, incfile);
288                 } else
289                         writefile = getFileName();
290                 writefile = ChangeExtension(writefile, ".tex");
291                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
292                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
293
294                 tmp->markDepClean(buffer->tmppath);
295
296                 tmp->makeLaTeXFile(writefile,
297                                    OnlyPath(getMasterFilename()),
298                                    buffer->niceFile, true);
299         }
300
301         if (isVerbatim()) {
302                 os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
303         } else if (params_.flag == INPUT) {
304                 // \input wants file with extension (default is .tex)
305                 if (!IsLyXFilename(getFileName())) {
306                         os << '\\' << params_.cparams.getCmdName() << '{' << incfile << '}';
307                 } else {
308                         os << '\\' << params_.cparams.getCmdName() << '{'
309                            << ChangeExtension(incfile, ".tex")
310                            <<  '}';
311                 }
312         } else {
313                 // \include don't want extension and demands that the
314                 // file really have .tex
315                 os << '\\' << params_.cparams.getCmdName() << '{'
316                    << ChangeExtension(incfile, string())
317                    << '}';
318         }
319
320         return 0;
321 }
322
323
324 int InsetInclude::ascii(Buffer const *, ostream & os, int) const
325 {
326         if (isVerbatim())
327                 os << GetFileContents(getFileName());
328         return 0;
329 }
330
331
332 int InsetInclude::linuxdoc(Buffer const * buffer, ostream & os) const
333 {
334         string incfile(params_.cparams.getContents());
335
336         // Do nothing if no file name has been specified
337         if (incfile.empty())
338                 return 0;
339
340         if (loadIfNeeded()) {
341                 Buffer * tmp = bufferlist.getBuffer(getFileName());
342
343                 // write it to a file (so far the complete file)
344                 string writefile = ChangeExtension(getFileName(), ".sgml");
345                 if (!buffer->tmppath.empty() && !buffer->niceFile) {
346                         incfile = subst(incfile, '/','@');
347                         writefile = AddName(buffer->tmppath, incfile);
348                 } else
349                         writefile = getFileName();
350
351                 if (IsLyXFilename(getFileName()))
352                         writefile = ChangeExtension(writefile, ".sgml");
353
354                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
355                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
356
357                 tmp->makeLinuxDocFile(writefile, buffer->niceFile, true);
358         }
359
360         if (isVerbatim()) {
361                 os << "<![CDATA["
362                    << GetFileContents(getFileName())
363                    << "]]>";
364         } else
365                 os << '&' << include_label << ';';
366
367         return 0;
368 }
369
370
371 int InsetInclude::docbook(Buffer const * buffer, ostream & os,
372                           bool /*mixcont*/) const
373 {
374         string incfile(params_.cparams.getContents());
375
376         // Do nothing if no file name has been specified
377         if (incfile.empty())
378                 return 0;
379
380         if (loadIfNeeded()) {
381                 Buffer * tmp = bufferlist.getBuffer(getFileName());
382
383                 // write it to a file (so far the complete file)
384                 string writefile = ChangeExtension(getFileName(), ".sgml");
385                 if (!buffer->tmppath.empty() && !buffer->niceFile) {
386                         incfile = subst(incfile, '/','@');
387                         writefile = AddName(buffer->tmppath, incfile);
388                 } else
389                         writefile = getFileName();
390                 if (IsLyXFilename(getFileName()))
391                         writefile = ChangeExtension(writefile, ".sgml");
392
393                 lyxerr[Debug::LATEX] << "incfile:" << incfile << endl;
394                 lyxerr[Debug::LATEX] << "writefile:" << writefile << endl;
395
396                 tmp->makeDocBookFile(writefile, buffer->niceFile, true);
397         }
398
399         if (isVerbatim()) {
400                 os << "<inlinegraphic fileref=\""
401                    << '&' << include_label << ';'
402                    << "\" format=\"linespecific\">";
403         } else
404                 os << '&' << include_label << ';';
405
406         return 0;
407 }
408
409
410 void InsetInclude::validate(LaTeXFeatures & features) const
411 {
412
413         string incfile(params_.cparams.getContents());
414         string writefile;
415
416         Buffer const * const b = bufferlist.getBuffer(getMasterFilename());
417
418         if (b && !b->tmppath.empty() && !b->niceFile && !isVerbatim()) {
419                 incfile = subst(incfile, '/','@');
420                 writefile = AddName(b->tmppath, incfile);
421         } else
422                 writefile = getFileName();
423
424         if (IsLyXFilename(getFileName()))
425                 writefile = ChangeExtension(writefile, ".sgml");
426
427         features.includeFile(include_label, writefile);
428
429         if (isVerbatim())
430                 features.require("verbatim");
431
432         // Here we must do the fun stuff...
433         // Load the file in the include if it needs
434         // to be loaded:
435         if (loadIfNeeded()) {
436                 // a file got loaded
437                 Buffer * const tmp = bufferlist.getBuffer(getFileName());
438                 if (tmp) {
439                         if (b)
440                                 tmp->niceFile = b->niceFile;
441                         tmp->validate(features);
442                 }
443         }
444 }
445
446
447 vector<string> const InsetInclude::getLabelList() const
448 {
449         vector<string> l;
450
451         if (loadIfNeeded()) {
452                 Buffer * tmp = bufferlist.getBuffer(getFileName());
453                 tmp->setParentName("");
454                 l = tmp->getLabelList();
455                 tmp->setParentName(getMasterFilename());
456         }
457
458         return l;
459 }
460
461
462 vector<pair<string,string> > const InsetInclude::getKeys() const
463 {
464         vector<pair<string,string> > keys;
465
466         if (loadIfNeeded()) {
467                 Buffer * tmp = bufferlist.getBuffer(getFileName());
468                 tmp->setParentName("");
469                 keys = tmp->getBibkeyList();
470                 tmp->setParentName(getMasterFilename());
471         }
472
473         return keys;
474 }
475
476
477 int InsetInclude::ascent(BufferView * bv, LyXFont const & font) const
478 {
479         return preview_->previewReady() ?
480                 preview_->pimage()->ascent() : InsetButton::ascent(bv, font);
481 }
482
483
484 int InsetInclude::descent(BufferView * bv, LyXFont const & font) const
485 {
486         return preview_->previewReady() ?
487                 preview_->pimage()->descent() : InsetButton::descent(bv, font);
488 }
489
490
491 int InsetInclude::width(BufferView * bv, LyXFont const & font) const
492 {
493         return preview_->previewReady() ?
494                 preview_->pimage()->width() : InsetButton::width(bv, font);
495 }
496
497
498 void InsetInclude::draw(BufferView * bv, LyXFont const & font, int y,
499                         float & xx, bool b) const
500 {
501         preview_->setView(bv);
502         if (!preview_->previewReady()) {
503                 InsetButton::draw(bv, font, y, xx, b);
504                 return;
505         }
506
507         int const x = int(xx);
508         int const w = width(bv, font);
509         int const d = descent(bv, font);
510         int const a = ascent(bv, font);
511         int const h = a + d;
512
513         bv->painter().image(x, y - a, w, h,
514                             *(preview_->pimage()->image(*this, *bv)));
515
516         xx += w;
517 }
518
519
520 //
521 // preview stuff
522 //
523
524 void InsetInclude::addPreview(grfx::PreviewLoader & ploader) const
525 {
526         preview_->addPreview(ploader);
527 }
528
529
530 bool InsetInclude::PreviewImpl::previewWanted() const
531 {
532         return parent().params_.flag == InsetInclude::INPUT &&
533                 parent().params_.cparams.preview();
534 }
535
536
537 string const InsetInclude::PreviewImpl::latexString() const
538 {
539         if (!view() || !view()->buffer())
540                 return string();
541
542         ostringstream os;
543         parent().latex(view()->buffer(), os, false, false);
544
545         // This fails if the file has a relative path.
546         // return os.str().c_str();
547
548         // I would /really/ like not to do this...
549         // HELP!
550         string command;
551         string file = rtrim(split(os.str().c_str(), command, '{'), "}");
552
553         if (!AbsolutePath(file))
554                 file = MakeAbsPath(file, view()->buffer()->filePath());
555
556         ostringstream out;
557         out << command << '{' << file << '}' << endl;
558         
559         return out.str().c_str();
560 }