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