]> git.lyx.org Git - features.git/blob - src/LaTeXFeatures.C
Fix output of change tracked documents (also fixes bug 1031)
[features.git] / src / LaTeXFeatures.C
1 /**
2  * \file LaTeXFeatures.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author José Matos
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Jürgen Vigna
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "LaTeXFeatures.h"
18
19 #include "bufferparams.h"
20 #include "debug.h"
21 #include "encoding.h"
22 #include "Floating.h"
23 #include "FloatList.h"
24 #include "language.h"
25 #include "lyxlex.h"
26 #include "lyx_sty.h"
27 #include "lyxrc.h"
28
29 #include "support/filetools.h"
30
31 #include <sstream>
32
33 using lyx::support::IsSGMLFilename;
34 using lyx::support::LibFileSearch;
35 using lyx::support::MakeRelPath;
36 using lyx::support::OnlyPath;
37
38 using std::endl;
39 using std::find;
40 using std::string;
41 using std::list;
42 using std::ostream;
43 using std::ostringstream;
44 using std::set;
45
46 namespace biblio = lyx::biblio;
47
48
49 LaTeXFeatures::PackagesList LaTeXFeatures::packages_;
50
51
52 LaTeXFeatures::LaTeXFeatures(Buffer const & b, BufferParams const & p, bool n)
53         : buffer_(&b), params_(p), nice_(n)
54 {}
55
56
57 bool LaTeXFeatures::useBabel() const
58 {
59         return lyxrc.language_use_babel ||
60                 bufferParams().language->lang() != lyxrc.default_language ||
61                 this->hasLanguages();
62 }
63
64
65 void LaTeXFeatures::require(string const & name)
66 {
67         if (isRequired(name))
68                 return;
69
70         features_.push_back(name);
71 }
72
73
74 void LaTeXFeatures::getAvailable()
75 {
76         LyXLex lex(0, 0);
77         string real_file = LibFileSearch("", "packages.lst");
78
79         if (real_file.empty())
80                 return;
81
82         lex.setFile(real_file);
83
84         if (!lex.isOK()) 
85                 return;
86
87         bool finished = false;
88         // Parse config-file
89         while (lex.isOK() && !finished) {
90                 switch (lex.lex()) {
91                 case LyXLex::LEX_FEOF:
92                         finished = true;
93                         break;
94                 default:
95                         string const name = lex.getString();
96                         PackagesList::const_iterator begin = packages_.begin();
97                         PackagesList::const_iterator end   = packages_.end();
98                         if (find(begin, end, name) == end)
99                                 packages_.push_back(name);
100                 }
101         }
102
103         return;
104 }
105
106
107 void LaTeXFeatures::useLayout(string const & layoutname)
108 {
109         // Some code to avoid loops in dependency definition
110         static int level = 0;
111         const int maxlevel = 30;
112         if (level > maxlevel) {
113                 lyxerr << "LaTeXFeatures::useLayout: maximum level of "
114                        << "recursion attained by layout "
115                        << layoutname << endl;
116                 return;
117         }
118
119         LyXTextClass const & tclass = params_.getLyXTextClass();
120         if (tclass.hasLayout(layoutname)) {
121                 // Is this layout already in usedLayouts?
122                 list<string>::const_iterator cit = usedLayouts_.begin();
123                 list<string>::const_iterator end = usedLayouts_.end();
124                 for (; cit != end; ++cit) {
125                         if (layoutname == *cit)
126                                 return;
127                 }
128
129                 LyXLayout_ptr const & lyt = tclass[layoutname];
130                 if (!lyt->depends_on().empty()) {
131                         ++level;
132                         useLayout(lyt->depends_on());
133                         --level;
134                 }
135                 usedLayouts_.push_back(layoutname);
136         } else {
137                 lyxerr << "LaTeXFeatures::useLayout: layout `"
138                        << layoutname << "' does not exist in this class"
139                        << endl;
140         }
141
142         --level;
143 }
144
145
146 bool LaTeXFeatures::isRequired(string const & name) const
147 {
148         return find(features_.begin(), features_.end(), name) != features_.end();
149 }
150
151
152 bool LaTeXFeatures::isAvailable(string const & name) const
153 {
154         if (packages_.empty())
155                 getAvailable();
156         return find(packages_.begin(), packages_.end(), name) != packages_.end();
157 }
158
159
160 void LaTeXFeatures::addExternalPreamble(string const & preamble)
161 {
162         FeaturesList::const_iterator begin = preamble_snippets_.begin();
163         FeaturesList::const_iterator end   = preamble_snippets_.end();
164         if (find(begin, end, preamble) == end)
165                 preamble_snippets_.push_back(preamble);
166 }
167
168
169 void LaTeXFeatures::useFloat(string const & name)
170 {
171         usedFloats_.insert(name);
172         // We only need float.sty if we use non builtin floats, or if we
173         // use the "H" modifier. This includes modified table and
174         // figure floats. (Lgb)
175         Floating const & fl = params_.getLyXTextClass().floats().getType(name);
176         if (!fl.type().empty() && !fl.builtin()) {
177                 require("float");
178         }
179 }
180
181
182 void LaTeXFeatures::useLanguage(Language const * lang)
183 {
184         UsedLanguages_.insert(lang);
185 }
186
187
188 void LaTeXFeatures::includeFile(string const & key, string const & name)
189 {
190         IncludedFiles_[key] = name;
191 }
192
193
194 bool LaTeXFeatures::hasLanguages() const
195 {
196         return !UsedLanguages_.empty();
197 }
198
199
200 string LaTeXFeatures::getLanguages() const
201 {
202         ostringstream languages;
203
204         for (LanguageList::const_iterator cit =
205                     UsedLanguages_.begin();
206              cit != UsedLanguages_.end();
207              ++cit)
208                 languages << (*cit)->babel() << ',';
209         return languages.str();
210 }
211
212
213 set<string> LaTeXFeatures::getEncodingSet(string const & doc_encoding) const
214 {
215         set<string> encodings;
216         LanguageList::const_iterator it  = UsedLanguages_.begin();
217         LanguageList::const_iterator end = UsedLanguages_.end();
218         for (; it != end; ++it)
219                 if ((*it)->encoding()->LatexName() != doc_encoding)
220                         encodings.insert((*it)->encoding()->LatexName());
221         return encodings;
222 }
223
224 namespace {
225
226 char const * simplefeatures[] = {
227         "array",
228         "verbatim",
229         "longtable",
230         "rotating",
231         "latexsym",
232         "pifont",
233         "subfigure",
234         "floatflt",
235         "varioref",
236         "prettyref",
237         "float",
238         "wasysym",
239         "dvipost",
240         "fancybox",
241         "calc",
242 };
243
244 int const nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
245
246 }
247
248
249 string const LaTeXFeatures::getPackages() const
250 {
251         ostringstream packages;
252         LyXTextClass const & tclass = params_.getLyXTextClass();
253
254         //
255         //  These are all the 'simple' includes.  i.e
256         //  packages which we just \usepackage{package}
257         //
258         for (int i = 0; i < nb_simplefeatures; ++i) {
259                 if (isRequired(simplefeatures[i]))
260                         packages << "\\usepackage{"
261                                  << simplefeatures[i] << "}\n";
262         }
263
264         //
265         // The rest of these packages are somewhat more complicated
266         // than those above.
267         //
268
269         if (isRequired("amsmath")
270             && !tclass.provides(LyXTextClass::amsmath)
271             && params_.use_amsmath != BufferParams::AMS_OFF) {
272                 packages << "\\usepackage{amsmath}\n";
273         }
274
275         // color.sty
276         if (isRequired("color")) {
277                 if (params_.graphicsDriver == "default")
278                         packages << "\\usepackage[usenames]{color}\n";
279                 else
280                         packages << "\\usepackage["
281                                  << params_.graphicsDriver
282                                  << ",usenames"
283                                  << "]{color}\n";
284         }
285
286         // makeidx.sty
287         if (isRequired("makeidx")) {
288                 if (! tclass.provides(LyXTextClass::makeidx))
289                         packages << "\\usepackage{makeidx}\n";
290                 packages << "\\makeindex\n";
291         }
292
293         // graphicx.sty
294         if (isRequired("graphicx") && params_.graphicsDriver != "none") {
295                 if (params_.graphicsDriver == "default")
296                         packages << "\\usepackage{graphicx}\n";
297                 else
298                         packages << "\\usepackage["
299                                  << params_.graphicsDriver
300                                  << "]{graphicx}\n";
301         }
302
303         //if (algorithm) {
304         //      packages << "\\usepackage{algorithm}\n";
305         //}
306
307         // lyxskak.sty --- newer chess support based on skak.sty
308         if (isRequired("chess")) {
309                 packages << "\\usepackage[ps,mover]{lyxskak}\n";
310         }
311
312         // setspace.sty
313         if ((params_.spacing().getSpace() != Spacing::Single
314              && !params_.spacing().isDefault())
315             || isRequired("setspace")) {
316                 packages << "\\usepackage{setspace}\n";
317         }
318         switch (params_.spacing().getSpace()) {
319         case Spacing::Default:
320         case Spacing::Single:
321                 // we dont use setspace.sty so dont print anything
322                 //packages += "\\singlespacing\n";
323                 break;
324         case Spacing::Onehalf:
325                 packages << "\\onehalfspacing\n";
326                 break;
327         case Spacing::Double:
328                 packages << "\\doublespacing\n";
329                 break;
330         case Spacing::Other:
331                 packages << "\\setstretch{"
332                          << params_.spacing().getValue() << "}\n";
333                 break;
334         }
335
336         // amssymb.sty
337         if (isRequired("amssymb") || params_.use_amsmath == BufferParams::AMS_ON)
338                 packages << "\\usepackage{amssymb}\n";
339         // url.sty
340         if (isRequired("url") && ! tclass.provides(LyXTextClass::url))
341                 packages << "\\IfFileExists{url.sty}{\\usepackage{url}}\n"
342                             "                      {\\newcommand{\\url}{\\texttt}}\n";
343
344         // float.sty
345         // natbib.sty
346         if (isRequired("natbib") && ! tclass.provides(LyXTextClass::natbib)) {
347                 packages << "\\usepackage[";
348                 if (params_.cite_engine == biblio::ENGINE_NATBIB_NUMERICAL) {
349                         packages << "numbers";
350                 } else {
351                         packages << "authoryear";
352                 }
353                 packages << "]{natbib}\n";
354         }
355
356         // jurabib -- we need version 0.6 at least.
357         if (isRequired("jurabib")) {
358                 packages << "\\usepackage{jurabib}[2004/01/25]\n";
359         }
360
361         // bibtopic -- the dot provides the aux file naming which
362         // LyX can detect.
363         if (isRequired("bibtopic")) {
364                 packages << "\\usepackage[dot]{bibtopic}\n";
365         }
366
367         return packages.str();
368 }
369
370
371 string const LaTeXFeatures::getMacros() const
372 {
373         ostringstream macros;
374
375         if (!preamble_snippets_.empty())
376                 macros << '\n';
377         FeaturesList::const_iterator pit  = preamble_snippets_.begin();
378         FeaturesList::const_iterator pend = preamble_snippets_.end();
379         for (; pit != pend; ++pit) {
380                 macros << *pit << '\n';
381         }
382
383         if (isRequired("LyX"))
384                 macros << lyx_def << '\n';
385
386         if (isRequired("lyxline"))
387                 macros << lyxline_def << '\n';
388
389         if (isRequired("noun"))
390                 macros << noun_def << '\n';
391
392         if (isRequired("lyxarrow"))
393                 macros << lyxarrow_def << '\n';
394
395         // quotes.
396         if (isRequired("quotesinglbase"))
397                 macros << quotesinglbase_def << '\n';
398         if (isRequired("quotedblbase"))
399                 macros << quotedblbase_def << '\n';
400         if (isRequired("guilsinglleft"))
401                 macros << guilsinglleft_def << '\n';
402         if (isRequired("guilsinglright"))
403                 macros << guilsinglright_def << '\n';
404         if (isRequired("guillemotleft"))
405                 macros << guillemotleft_def << '\n';
406         if (isRequired("guillemotright"))
407                 macros << guillemotright_def << '\n';
408
409         // Math mode
410         if (isRequired("boldsymbol") && !isRequired("amsmath"))
411                 macros << boldsymbol_def << '\n';
412         if (isRequired("binom") && !isRequired("amsmath"))
413                 macros << binom_def << '\n';
414         if (isRequired("mathcircumflex"))
415                 macros << mathcircumflex_def << '\n';
416
417         // other
418         if (isRequired("ParagraphLeftIndent"))
419                 macros << paragraphleftindent_def;
420         if (isRequired("NeedLyXFootnoteCode"))
421                 macros << floatingfootnote_def;
422
423         // some problems with tex->html converters
424         if (isRequired("NeedTabularnewline"))
425                 macros << tabularnewline_def;
426
427         // greyedout environment (note inset)
428         if (isRequired("lyxgreyedout"))
429                 macros << lyxgreyedout_def;
430
431         if (isRequired("lyxdot"))
432                 macros << lyxdot_def << '\n';
433
434         // floats
435         getFloatDefinitions(macros);
436
437         return macros.str();
438 }
439
440
441 string const LaTeXFeatures::getBabelOptions() const
442 {
443         ostringstream tmp;
444
445         LanguageList::const_iterator it  = UsedLanguages_.begin();
446         LanguageList::const_iterator end =  UsedLanguages_.end();
447         for (; it != end; ++it)
448                 if (!(*it)->latex_options().empty())
449                         tmp << (*it)->latex_options() << '\n';
450         if (!params_.language->latex_options().empty())
451                 tmp << params_.language->latex_options() << '\n';
452
453         return tmp.str();
454 }
455
456
457 string const LaTeXFeatures::getTClassPreamble() const
458 {
459         // the text class specific preamble
460         LyXTextClass const & tclass = params_.getLyXTextClass();
461         ostringstream tcpreamble;
462
463         tcpreamble << tclass.preamble();
464
465         list<string>::const_iterator cit = usedLayouts_.begin();
466         list<string>::const_iterator end = usedLayouts_.end();
467         for (; cit != end; ++cit) {
468                 tcpreamble << tclass[*cit]->preamble();
469         }
470
471         CharStyles::iterator cs = tclass.charstyles().begin();
472         CharStyles::iterator csend = tclass.charstyles().end();
473         for (; cs != csend; ++cs) {
474                 if (isRequired(cs->name))
475                         tcpreamble << cs->preamble;
476         }
477
478         return tcpreamble.str();
479 }
480
481
482 string const LaTeXFeatures::getLyXSGMLEntities() const
483 {
484         // Definition of entities used in the document that are LyX related.
485         ostringstream entities;
486
487         if (isRequired("lyxarrow")) {
488                 entities << "<!ENTITY lyxarrow \"-&gt;\">" << '\n';
489         }
490
491         return entities.str();
492 }
493
494
495 string const LaTeXFeatures::getIncludedFiles(string const & fname) const
496 {
497         ostringstream sgmlpreamble;
498         string const basename = OnlyPath(fname);
499
500         FileMap::const_iterator end = IncludedFiles_.end();
501         for (FileMap::const_iterator fi = IncludedFiles_.begin();
502              fi != end; ++fi)
503                 sgmlpreamble << "\n<!ENTITY " << fi->first
504                              << (IsSGMLFilename(fi->second) ? " SYSTEM \"" : " \"")
505                              << MakeRelPath(fi->second, basename) << "\">";
506
507         return sgmlpreamble.str();
508 }
509
510
511 void LaTeXFeatures::showStruct() const {
512         lyxerr << "LyX needs the following commands when LaTeXing:"
513                << "\n***** Packages:" << getPackages()
514                << "\n***** Macros:" << getMacros()
515                << "\n***** Textclass stuff:" << getTClassPreamble()
516                << "\n***** done." << endl;
517 }
518
519
520 Buffer const & LaTeXFeatures::buffer() const
521 {
522         return *buffer_;
523 }
524
525
526 void LaTeXFeatures::setBuffer(Buffer const & buffer)
527 {
528         buffer_ = &buffer;
529 }
530
531
532 BufferParams const & LaTeXFeatures::bufferParams() const
533 {
534         return params_;
535 }
536
537
538 void LaTeXFeatures::getFloatDefinitions(ostream & os) const
539 {
540         FloatList const & floats = params_.getLyXTextClass().floats();
541
542         // Here we will output the code to create the needed float styles.
543         // We will try to do this as minimal as possible.
544         // \floatstyle{ruled}
545         // \newfloat{algorithm}{htbp}{loa}
546         // \floatname{algorithm}{Algorithm}
547         UsedFloats::const_iterator cit = usedFloats_.begin();
548         UsedFloats::const_iterator end = usedFloats_.end();
549         // ostringstream floats;
550         for (; cit != end; ++cit) {
551                 Floating const & fl = floats.getType((*cit));
552
553                 // For builtin floats we do nothing.
554                 if (fl.builtin()) continue;
555
556                 // We have to special case "table" and "figure"
557                 if (fl.type() == "tabular" || fl.type() == "figure") {
558                         // Output code to modify "table" or "figure"
559                         // but only if builtin == false
560                         // and that have to be true at this point in the
561                         // function.
562                         string const type = fl.type();
563                         string const placement = fl.placement();
564                         string const style = fl.style();
565                         if (!style.empty()) {
566                                 os << "\\floatstyle{" << style << "}\n"
567                                    << "\\restylefloat{" << type << "}\n";
568                         }
569                         if (!placement.empty()) {
570                                 os << "\\floatplacement{" << type << "}{"
571                                    << placement << "}\n";
572                         }
573                 } else {
574                         // The other non builtin floats.
575
576                         string const type = fl.type();
577                         string const placement = fl.placement();
578                         string const ext = fl.ext();
579                         string const within = fl.within();
580                         string const style = fl.style();
581                         string const name = fl.name();
582                         os << "\\floatstyle{" << style << "}\n"
583                            << "\\newfloat{" << type << "}{" << placement
584                            << "}{" << ext << '}';
585                         if (!within.empty())
586                                 os << '[' << within << ']';
587                         os << '\n'
588                            << "\\floatname{" << type << "}{"
589                            << name << "}\n";
590
591                         // What missing here is to code to minimalize the code
592                         // output so that the same floatstyle will not be
593                         // used several times, when the same style is still in
594                         // effect. (Lgb)
595                 }
596         }
597 }