]> git.lyx.org Git - lyx.git/blob - src/LaTeXFeatures.C
Fix natbib bug spotted by JMarc.
[lyx.git] / src / LaTeXFeatures.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 the LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include "LaTeXFeatures.h"
18 #include "debug.h"
19 #include "lyx_sty.h"
20 #include "lyxrc.h"
21 #include "bufferparams.h"
22 #include "FloatList.h"
23 #include "language.h"
24 #include "encoding.h"
25 #include "LString.h"
26
27 #include "support/filetools.h"
28 #include "support/lstrings.h"
29
30 using lyx::textclass_type;
31
32 using std::endl;
33 using std::list;
34 using std::set;
35 using std::find;
36 using std::ostream;
37
38
39 LaTeXFeatures::LaTeXFeatures(BufferParams const & p)
40         : params(p)
41 {}
42
43
44 void LaTeXFeatures::require(string const & name)
45 {
46         if (isRequired(name))
47                 return;
48
49         features.push_back(name);
50 }
51
52
53 void LaTeXFeatures::useLayout(string const & layoutname)
54 {
55         // Some code to avoid loops in dependency definition
56         static int level = 0;
57         const int maxlevel = 30;
58         if (level > maxlevel) {
59                 lyxerr << "LaTeXFeatures::useLayout: maximum level of "
60                        << "recursion attained by layout "
61                        << layoutname << endl;
62                 return;
63         }
64
65         LyXTextClass const & tclass = params.getLyXTextClass();
66         if (tclass.hasLayout(layoutname)) {
67                 // Is this layout already in usedLayouts?
68                 list<string>::const_iterator cit = usedLayouts.begin();
69                 list<string>::const_iterator end = usedLayouts.end();
70                 for (; cit != end; ++cit) {
71                         if (layoutname == *cit)
72                                 return;
73                 }
74
75                 LyXLayout_ptr const & lyt = tclass[layoutname];
76                 if (!lyt->depends_on().empty()) {
77                         ++level;
78                         useLayout(lyt->depends_on());
79                         --level;
80                 }
81                 usedLayouts.push_back(layoutname);
82         } else {
83                 lyxerr << "LaTeXFeatures::useLayout: layout `"
84                        << layoutname << "' does not exist in this class"
85                        << endl;
86         }
87
88         --level;
89 }
90
91
92 bool LaTeXFeatures::isRequired(string const & name) const
93 {
94         return find(features.begin(), features.end(), name) != features.end();
95 }
96
97
98 void LaTeXFeatures::addExternalPreamble(string const & pream)
99 {
100         externalPreambles += pream;
101 }
102
103
104 void LaTeXFeatures::useFloat(string const & name)
105 {
106         usedFloats.insert(name);
107         // We only need float.sty if we use non builtin floats, or if we
108         // use the "H" modifier. This includes modified table and
109         // figure floats. (Lgb)
110         Floating const & fl = params.getLyXTextClass().floats().getType(name);
111         if (!fl.type().empty() && !fl.builtin()) {
112                 require("float");
113         }
114 }
115
116
117 void LaTeXFeatures::useLanguage(Language const * lang)
118 {
119         UsedLanguages.insert(lang);
120 }
121
122
123 void LaTeXFeatures::includeFile(string const & key, string const & name)
124 {
125         IncludedFiles[key] = name;
126 }
127
128
129 bool LaTeXFeatures::hasLanguages()
130 {
131         return !UsedLanguages.empty();
132 }
133
134
135 string LaTeXFeatures::getLanguages() const
136 {
137         ostringstream languages;
138
139         for (LanguageList::const_iterator cit =
140                     UsedLanguages.begin();
141              cit != UsedLanguages.end();
142              ++cit)
143                 languages << (*cit)->babel() << ',';
144
145         return STRCONV(languages.str());
146 }
147
148
149 set<string> LaTeXFeatures::getEncodingSet(string const & doc_encoding)
150 {
151         set<string> encodings;
152         for (LanguageList::const_iterator it =
153                      UsedLanguages.begin();
154              it != UsedLanguages.end(); ++it)
155                 if ((*it)->encoding()->LatexName() != doc_encoding)
156                         encodings.insert((*it)->encoding()->LatexName());
157         return encodings;
158 }
159
160 namespace {
161
162 char const * simplefeatures[] = {
163         "array",
164         "verbatim",
165         "longtable",
166         "rotating",
167         "latexsym",
168         "pifont",
169         "subfigure",
170         "floatflt",
171         "varioref",
172         "prettyref",
173         "float",
174         "wasy",
175         "dvipost"
176 };
177
178 int const nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
179
180 }
181
182 string const LaTeXFeatures::getPackages() const
183 {
184         ostringstream packages;
185         LyXTextClass const & tclass = params.getLyXTextClass();
186
187
188         //
189         //  These are all the 'simple' includes.  i.e
190         //  packages which we just \usepackage{package}
191         //
192         for (int i = 0; i < nb_simplefeatures; ++i) {
193                 if (isRequired(simplefeatures[i]))
194                         packages << "\\usepackage{"
195                                  << simplefeatures[i] << "}\n";
196         }
197
198         //
199         // The rest of these packages are somewhat more complicated
200         // than those above.
201         //
202
203         if (isRequired("amsmath")
204             && ! tclass.provides(LyXTextClass::amsmath)) {
205                 packages << "\\usepackage{amsmath}\n";
206         }
207
208         // color.sty
209         if (isRequired("color")) {
210                 if (params.graphicsDriver == "default")
211                         packages << "\\usepackage[usenames]{color}\n";
212                 else
213                         packages << "\\usepackage["
214                                  << params.graphicsDriver
215                                  << ",usenames"
216                                  << "]{color}\n";
217         }
218
219         // makeidx.sty
220         if (isRequired("makeidx")) {
221                 if (! tclass.provides(LyXTextClass::makeidx))
222                         packages << "\\usepackage{makeidx}\n";
223                 packages << "\\makeindex\n";
224         }
225
226         // graphicx.sty
227         if (isRequired("graphicx") && params.graphicsDriver != "none") {
228                 if (params.graphicsDriver == "default")
229                         packages << "\\usepackage{graphicx}\n";
230                 else
231                         packages << "\\usepackage["
232                                  << params.graphicsDriver
233                                  << "]{graphicx}\n";
234         }
235
236         //if (algorithm) {
237         //      packages << "\\usepackage{algorithm}\n";
238         //}
239
240         // lyxskak.sty --- newer chess support based on skak.sty
241         if (isRequired("chess")) {
242                 packages << "\\usepackage[ps,mover]{lyxskak}\n";
243         }
244
245         // setspace.sty
246         if ((params.spacing.getSpace() != Spacing::Single
247              && !params.spacing.isDefault())
248             || isRequired("setspace")) {
249                 packages << "\\usepackage{setspace}\n";
250         }
251         switch (params.spacing.getSpace()) {
252         case Spacing::Default:
253         case Spacing::Single:
254                 // we dont use setspace.sty so dont print anything
255                 //packages += "\\singlespacing\n";
256                 break;
257         case Spacing::Onehalf:
258                 packages << "\\onehalfspacing\n";
259                 break;
260         case Spacing::Double:
261                 packages << "\\doublespacing\n";
262                 break;
263         case Spacing::Other:
264                 packages << "\\setstretch{"
265                          << params.spacing.getValue() << "}\n";
266                 break;
267         }
268
269         // amssymb.sty
270         if (isRequired("amssymb") || params.use_amsmath)
271                 packages << "\\usepackage{amssymb}\n";
272         // url.sty
273         if (isRequired("url") && ! tclass.provides(LyXTextClass::url))
274                 packages << "\\IfFileExists{url.sty}{\\usepackage{url}}\n"
275                             "                      {\\newcommand{\\url}{\\texttt}}\n";
276
277         // float.sty
278         // natbib.sty
279         if (isRequired("natbib") && ! tclass.provides(LyXTextClass::natbib)) {
280                 packages << "\\usepackage[";
281                 if (params.use_numerical_citations) {
282                         packages << "numbers";
283                 } else {
284                         packages << "authoryear";
285                 }
286                 packages << "]{natbib}\n";
287         }
288
289         packages << externalPreambles;
290
291         return STRCONV(packages.str());
292 }
293
294
295 string const LaTeXFeatures::getMacros() const
296 {
297         ostringstream macros;
298
299         if (isRequired("LyX"))
300                 macros << lyx_def << '\n';
301
302         if (isRequired("lyxline"))
303                 macros << lyxline_def << '\n';
304
305         if (isRequired("noun"))
306                 macros << noun_def << '\n';
307
308         if (isRequired("lyxarrow"))
309                 macros << lyxarrow_def << '\n';
310
311         // quotes.
312         if (isRequired("quotesinglbase"))
313                 macros << quotesinglbase_def << '\n';
314         if (isRequired("quotedblbase"))
315                 macros << quotedblbase_def << '\n';
316         if (isRequired("guilsinglleft"))
317                 macros << guilsinglleft_def << '\n';
318         if (isRequired("guilsinglright"))
319                 macros << guilsinglright_def << '\n';
320         if (isRequired("guillemotleft"))
321                 macros << guillemotleft_def << '\n';
322         if (isRequired("guillemotright"))
323                 macros << guillemotright_def << '\n';
324
325         // Math mode
326         if (isRequired("boldsymbol") && !isRequired("amsmath"))
327                 macros << boldsymbol_def << '\n';
328         if (isRequired("binom") && !isRequired("amsmath"))
329                 macros << binom_def << '\n';
330         if (isRequired("mathcircumflex"))
331                 macros << mathcircumflex_def << '\n';
332
333         // other
334         if (isRequired("NeedLyXMinipageIndent"))
335                 macros << minipageindent_def;
336         if (isRequired("ParagraphLeftIndent"))
337                 macros << paragraphleftindent_def;
338         if (isRequired("NeedLyXFootnoteCode"))
339                 macros << floatingfootnote_def;
340
341         // some problems with tex->html converters
342         if (isRequired("NeedTabularnewline"))
343                 macros << tabularnewline_def;
344
345         // floats
346         getFloatDefinitions(macros);
347
348         return STRCONV(macros.str());
349 }
350
351
352 string const LaTeXFeatures::getBabelOptions() const
353 {
354         ostringstream tmp;
355
356         for (LanguageList::const_iterator cit = UsedLanguages.begin();
357              cit != UsedLanguages.end(); ++cit)
358                 if (!(*cit)->latex_options().empty())
359                         tmp << (*cit)->latex_options() << '\n';
360         if (!params.language->latex_options().empty())
361                 tmp << params.language->latex_options() << '\n';
362
363         return STRCONV(tmp.str());
364 }
365
366
367 string const LaTeXFeatures::getTClassPreamble() const
368 {
369         // the text class specific preamble
370         LyXTextClass const & tclass = params.getLyXTextClass();
371         ostringstream tcpreamble;
372
373         tcpreamble << tclass.preamble();
374
375         list<string>::const_iterator cit = usedLayouts.begin();
376         list<string>::const_iterator end = usedLayouts.end();
377         for (; cit != end; ++cit) {
378                 tcpreamble << tclass[*cit]->preamble();
379         }
380
381         return STRCONV(tcpreamble.str());
382 }
383
384
385 string const LaTeXFeatures::getLyXSGMLEntities() const
386 {
387         // Definition of entities used in the document that are LyX related.
388         ostringstream entities;
389
390         if (isRequired("lyxarrow")) {
391                 entities << "<!ENTITY lyxarrow \"-&gt;\">" << '\n';
392         }
393
394         return STRCONV(entities.str());
395 }
396
397
398 string const LaTeXFeatures::getIncludedFiles(string const & fname) const
399 {
400         ostringstream sgmlpreamble;
401         string const basename = OnlyPath(fname);
402
403         FileMap::const_iterator end = IncludedFiles.end();
404         for (FileMap::const_iterator fi = IncludedFiles.begin();
405              fi != end; ++fi)
406                 sgmlpreamble << "\n<!ENTITY " << fi->first
407                              << (IsSGMLFilename(fi->second) ? " SYSTEM \"" : " \"")
408                              << MakeRelPath(fi->second, basename) << "\">";
409
410         return STRCONV(sgmlpreamble.str());
411 }
412
413
414 void LaTeXFeatures::showStruct() const {
415         lyxerr << "LyX needs the following commands when LaTeXing:"
416                << "\n***** Packages:" << getPackages()
417                << "\n***** Macros:" << getMacros()
418                << "\n***** Textclass stuff:" << getTClassPreamble()
419                << "\n***** done." << endl;
420 }
421
422
423 BufferParams const & LaTeXFeatures::bufferParams() const
424 {
425         return params;
426 }
427
428
429 void LaTeXFeatures::getFloatDefinitions(ostream & os) const
430 {
431         FloatList const & floats = params.getLyXTextClass().floats();
432
433         // Here we will output the code to create the needed float styles.
434         // We will try to do this as minimal as possible.
435         // \floatstyle{ruled}
436         // \newfloat{algorithm}{htbp}{loa}
437         // \floatname{algorithm}{Algorithm}
438         UsedFloats::const_iterator cit = usedFloats.begin();
439         UsedFloats::const_iterator end = usedFloats.end();
440         // ostringstream floats;
441         for (; cit != end; ++cit) {
442                 Floating const & fl = floats.getType((*cit));
443
444                 // For builtin floats we do nothing.
445                 if (fl.builtin()) continue;
446
447                 // We have to special case "table" and "figure"
448                 if (fl.type() == "tabular" || fl.type() == "figure") {
449                         // Output code to modify "table" or "figure"
450                         // but only if builtin == false
451                         // and that have to be true at this point in the
452                         // function.
453                         string const type = fl.type();
454                         string const placement = fl.placement();
455                         string const style = fl.style();
456                         if (!style.empty()) {
457                                 os << "\\floatstyle{" << style << "}\n"
458                                    << "\\restylefloat{" << type << "}\n";
459                         }
460                         if (!placement.empty()) {
461                                 os << "\\floatplacement{" << type << "}{"
462                                    << placement << "}\n";
463                         }
464                 } else {
465                         // The other non builtin floats.
466
467                         string const type = fl.type();
468                         string const placement = fl.placement();
469                         string const ext = fl.ext();
470                         string const within = fl.within();
471                         string const style = fl.style();
472                         string const name = fl.name();
473                         os << "\\floatstyle{" << style << "}\n"
474                            << "\\newfloat{" << type << "}{" << placement
475                            << "}{" << ext << '}';
476                         if (!within.empty())
477                                 os << '[' << within << ']';
478                         os << '\n'
479                            << "\\floatname{" << type << "}{"
480                            << name << "}\n";
481
482                         // What missing here is to code to minimalize the code
483                         // output so that the same floatstyle will not be
484                         // used several times, when the same style is still in
485                         // effect. (Lgb)
486                 }
487         }
488 }