]> git.lyx.org Git - features.git/blob - src/LaTeXFeatures.cpp
Load mhchem if it is requested unconditionally, fix some typos
[features.git] / src / LaTeXFeatures.cpp
1 /**
2  * \file LaTeXFeatures.cpp
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 "Buffer.h"
20 #include "BufferParams.h"
21 #include "ColorSet.h"
22 #include "Converter.h"
23 #include "Encoding.h"
24 #include "Floating.h"
25 #include "FloatList.h"
26 #include "Language.h"
27 #include "Layout.h"
28 #include "Lexer.h"
29 #include "LyXRC.h"
30 #include "TextClass.h"
31
32 #include "insets/InsetLayout.h"
33
34 #include "support/debug.h"
35 #include "support/docstream.h"
36 #include "support/FileName.h"
37 #include "support/filetools.h"
38 #include "support/gettext.h"
39 #include "support/lstrings.h"
40
41 #include <algorithm>
42
43
44 using namespace std;
45 using namespace lyx::support;
46
47
48 namespace lyx {
49
50 /////////////////////////////////////////////////////////////////////
51 //
52 // Strings
53 //
54 /////////////////////////////////////////////////////////////////////
55
56 //\NeedsTeXFormat{LaTeX2e}
57 //\ProvidesPackage{lyx}[1996/01/11 LLE v0.2 (LyX LaTeX Extensions)]
58 //\message{LyX LaTeX Extensions (LLE v0.2) of 11-Jan-1996.}
59
60 static docstring const lyx_def = from_ascii(
61         "\\providecommand{\\LyX}{L\\kern-.1667em\\lower.25em\\hbox{Y}\\kern-.125emX\\@}");
62
63 static docstring const noun_def = from_ascii(
64         "\\newcommand{\\noun}[1]{\\textsc{#1}}");
65
66 static docstring const lyxarrow_def = from_ascii(
67         "\\DeclareRobustCommand*{\\lyxarrow}{%\n"
68         "\\@ifstar\n"
69         "{\\leavevmode\\,$\\triangleleft$\\,\\allowbreak}\n"
70         "{\\leavevmode\\,$\\triangleright$\\,\\allowbreak}}");
71
72 // for quotes without babel. This does not give perfect results, but
73 // anybody serious about non-english quotes should use babel (JMarc).
74
75 static docstring const quotedblbase_def = from_ascii(
76         "\\ProvideTextCommandDefault{\\quotedblbase}{%\n"
77         "  \\raisebox{-1.4ex}[1ex][.5ex]{\\textquotedblright}%\n"
78         "  \\penalty10000\\hskip0em\\relax%\n"
79         "}");
80
81 static docstring const quotesinglbase_def = from_ascii(
82         "\\ProvideTextCommandDefault{\\quotesinglbase}{%\n"
83         "  \\raisebox{-1.4ex}[1ex][.5ex]{\\textquoteright}%\n"
84         "  \\penalty10000\\hskip0em\\relax%\n"
85         "}");
86
87 static docstring const guillemotleft_def = from_ascii(
88         "\\ProvideTextCommandDefault{\\guillemotleft}{%\n"
89         "  {\\usefont{U}{lasy}{m}{n}\\char'50\\kern-.15em\\char'50}%\n"
90         "\\penalty10000\\hskip0pt\\relax%\n"
91         "}");
92
93 static docstring const guillemotright_def = from_ascii(
94         "\\ProvideTextCommandDefault{\\guillemotright}{%\n"
95         "  \\penalty10000\\hskip0pt%\n"
96         "  {\\usefont{U}{lasy}{m}{n}\\char'51\\kern-.15em\\char'51}%\n"
97         "}");
98
99 static docstring const guilsinglleft_def = from_ascii(
100         "\\ProvideTextCommandDefault{\\guilsinglleft}{%\n"
101         "  {\\usefont{U}{lasy}{m}{n}\\char'50}%\n"
102         "  \\penalty10000\\hskip0pt\\relax%\n"
103         "}");
104
105 static docstring const guilsinglright_def = from_ascii(
106         "\\ProvideTextCommandDefault{\\guilsinglright}{%\n"
107         "  \\penalty10000\\hskip0pt%\n"
108         "  {\\usefont{U}{lasy}{m}{n}\\char'51}%\n"
109         "}");
110
111 static docstring const paragraphleftindent_def = from_ascii(
112         "\\newenvironment{LyXParagraphLeftIndent}[1]%\n"
113         "{\n"
114         "  \\begin{list}{}{%\n"
115         "    \\setlength{\\topsep}{0pt}%\n"
116         "    \\addtolength{\\leftmargin}{#1}\n"
117 // ho hum, yet more things commented out with no hint as to why they
118 // weren't just removed
119 //      "%%    \\addtolength{\\leftmargin}{#1\\textwidth}\n"
120 //      "%%    \\setlength{\\textwidth}{#2\\textwidth}\n"
121 //      "%%    \\setlength\\listparindent\\parindent%\n"
122 //      "%%    \\setlength\\itemindent\\parindent%\n"
123         "    \\setlength{\\parsep}{0pt plus 1pt}%\n"
124         "  }\n"
125         "  \\item[]\n"
126         "}\n"
127         "{\\end{list}}\n");
128
129 static docstring const floatingfootnote_def = from_ascii(
130         "%% Special footnote code from the package 'stblftnt.sty'\n"
131         "%% Author: Robin Fairbairns -- Last revised Dec 13 1996\n"
132         "\\let\\SF@@footnote\\footnote\n"
133         "\\def\\footnote{\\ifx\\protect\\@typeset@protect\n"
134         "    \\expandafter\\SF@@footnote\n"
135         "  \\else\n"
136         "    \\expandafter\\SF@gobble@opt\n"
137         "  \\fi\n"
138         "}\n"
139         "\\expandafter\\def\\csname SF@gobble@opt \\endcsname{\\@ifnextchar[%]\n"
140         "  \\SF@gobble@twobracket\n"
141         "  \\@gobble\n"
142         "}\n"
143         "\\edef\\SF@gobble@opt{\\noexpand\\protect\n"
144         "  \\expandafter\\noexpand\\csname SF@gobble@opt \\endcsname}\n"
145         "\\def\\SF@gobble@twobracket[#1]#2{}\n");
146
147 static docstring const binom_def = from_ascii(
148         "%% Binom macro for standard LaTeX users\n"
149         "\\newcommand{\\binom}[2]{{#1 \\choose #2}}\n");
150
151 static docstring const mathcircumflex_def = from_ascii(
152         "%% For printing a cirumflex inside a formula\n"
153         "\\newcommand{\\mathcircumflex}[0]{\\mbox{\\^{}}}\n");
154
155 static docstring const tabularnewline_def = from_ascii(
156         "%% Because html converters don't know tabularnewline\n"
157         "\\providecommand{\\tabularnewline}{\\\\}\n");
158         
159 static docstring const lyxgreyedout_def = from_ascii(
160         "%% The greyedout annotation environment\n"
161         "\\newenvironment{lyxgreyedout}{\\textcolor{note_fontcolor}\\bgroup}{\\egroup}\n");
162
163 // We want to omit the file extension for includegraphics, but this does not
164 // work when the filename contains other dots.
165 // Idea from http://www.tex.ac.uk/cgi-bin/texfaq2html?label=unkgrfextn
166 static docstring const lyxdot_def = from_ascii(
167         "%% A simple dot to overcome graphicx limitations\n"
168         "\\newcommand{\\lyxdot}{.}\n");
169
170 static docstring const changetracking_dvipost_def = from_ascii(
171         "%% Change tracking with dvipost\n"
172         "\\dvipostlayout\n"
173         "\\dvipost{osstart color push Red}\n"
174         "\\dvipost{osend color pop}\n"
175         "\\dvipost{cbstart color push Blue}\n"
176         "\\dvipost{cbend color pop}\n"
177         "\\newcommand{\\lyxadded}[3]{\\changestart#3\\changeend}\n"
178         "\\newcommand{\\lyxdeleted}[3]{%\n"
179         "\\changestart\\overstrikeon#3\\overstrikeoff\\changeend}\n");
180
181 static docstring const changetracking_xcolor_ulem_def = from_ascii(
182         "%% Change tracking with ulem\n"
183         "\\newcommand{\\lyxadded}[3]{{\\color{lyxadded}{}#3}}\n"
184         "\\newcommand{\\lyxdeleted}[3]{{\\color{lyxdeleted}\\sout{#3}}}\n");
185
186 static docstring const changetracking_xcolor_ulem_hyperref_def = from_ascii(
187         "%% Change tracking with ulem\n"
188         "\\newcommand{\\lyxadded}[3]{{\\texorpdfstring{\\color{lyxadded}{}}{}#3}}\n"
189         "\\newcommand{\\lyxdeleted}[3]{{\\texorpdfstring{\\color{lyxdeleted}\\sout{#3}}{}}}\n");
190
191 static docstring const changetracking_none_def = from_ascii(
192         "\\newcommand{\\lyxadded}[3]{#3}\n"
193         "\\newcommand{\\lyxdeleted}[3]{}\n");
194
195 static docstring const textgreek_def = from_ascii(
196         "\\DeclareRobustCommand{\\greektext}{%\n"
197         "  \\fontencoding{LGR}\\selectfont\\def\\encodingdefault{LGR}}\n"
198         "\\DeclareRobustCommand{\\textgreek}[1]{\\leavevmode{\\greektext #1}}\n"
199         "\\DeclareFontEncoding{LGR}{}{}\n"
200         "\\DeclareTextSymbol{\\~}{LGR}{126}");
201
202 static docstring const textcyr_def = from_ascii(
203         "\\DeclareRobustCommand{\\cyrtext}{%\n"
204         "  \\fontencoding{T2A}\\selectfont\\def\\encodingdefault{T2A}}\n"
205         "\\DeclareRobustCommand{\\textcyr}[1]{\\leavevmode{\\cyrtext #1}}\n"
206         "\\AtBeginDocument{\\DeclareFontEncoding{T2A}{}{}}\n");
207
208 static docstring const lyxmathsym_def = from_ascii(
209         "\\newcommand{\\lyxmathsym}[1]{\\ifmmode\\begingroup\\def\\b@ld{bold}\n"
210         "  \\text{\\ifx\\math@version\\b@ld\\bfseries\\fi#1}\\endgroup\\else#1\\fi}\n");
211
212 static docstring const papersizedvi_def = from_ascii(
213         "\\special{papersize=\\the\\paperwidth,\\the\\paperheight}\n");
214
215 static docstring const papersizepdf_def = from_ascii(
216         "\\pdfpageheight\\paperheight\n"
217         "\\pdfpagewidth\\paperwidth\n");
218
219 static docstring const cedilla_def = from_ascii(
220         "\\newcommand{\\docedilla}[2]{\\underaccent{#1\\mathchar'30}{#2}}\n"
221         "\\newcommand{\\cedilla}[1]{\\mathpalette\\docedilla{#1}}\n");
222
223 static docstring const subring_def = from_ascii(
224         "\\newcommand{\\dosubring}[2]{\\underaccent{#1\\mathchar'27}{#2}}\n"
225         "\\newcommand{\\subring}[1]{\\mathpalette\\dosubring{#1}}\n");
226
227 static docstring const subdot_def = from_ascii(
228         "\\newcommand{\\dosubdot}[2]{\\underaccent{#1.}{#2}}\n"
229         "\\newcommand{\\subdot}[1]{\\mathpalette\\dosubdot{#1}}\n");
230
231 static docstring const subhat_def = from_ascii(
232         "\\newcommand{\\dosubhat}[2]{\\underaccent{#1\\mathchar'136}{#2}}\n"
233         "\\newcommand{\\subhat}[1]{\\mathpalette\\dosubhat{#1}}\n");
234
235 static docstring const subtilde_def = from_ascii(
236         "\\newcommand{\\dosubtilde}[2]{\\underaccent{#1\\mathchar'176}{#2}}\n"
237         "\\newcommand{\\subtilde}[1]{\\mathpalette\\dosubtilde{#1}}\n");
238
239 static docstring const dacute_def = from_ascii(
240         "\\DeclareMathAccent{\\dacute}{\\mathalpha}{operators}{'175}\n");
241
242 static docstring const tipasymb_def = from_ascii(
243         "\\DeclareFontEncoding{T3}{}{}\n"
244         "\\DeclareSymbolFont{tipasymb}{T3}{cmr}{m}{n}\n");
245
246 static docstring const dgrave_def = from_ascii(
247         "\\DeclareMathAccent{\\dgrave}{\\mathord}{tipasymb}{'15}\n");
248
249 static docstring const rcap_def = from_ascii(
250         "\\DeclareMathAccent{\\rcap}{\\mathord}{tipasymb}{'20}\n");
251
252 static docstring const ogonek_def = from_ascii(
253         "\\newcommand{\\doogonek}[2]{\\setbox0=\\hbox{$#1#2$}\\underaccent{#1\\mkern-6mu\n"
254         "  \\ifx#2O\\hskip0.5\\wd0\\else\\ifx#2U\\hskip0.5\\wd0\\else\\hskip\\wd0\\fi\\fi\n"
255         "  \\ifx#2o\\mkern-2mu\\else\\ifx#2e\\mkern-1mu\\fi\\fi\n"
256         "  \\mathchar\"0\\hexnumber@\\symtipasymb0C}{#2}}\n"
257         "\\newcommand{\\ogonek}[1]{\\mathpalette\\doogonek{#1}}\n");
258
259 static docstring const lyxref_def = from_ascii(
260                 "\\RS@ifundefined{subref}\n"
261                 "  {\\def\\RSsubtxt{section~}\\newref{sub}{name = \\RSsubtxt}}\n" 
262                 "  {}\n"
263                 "\\RS@ifundefined{thmref}\n"
264                 "  {\\def\\RSthmtxt{theorem~}\\newref{thm}{name = \\RSthmtxt}}\n" 
265                 "  {}\n"
266                 "\\RS@ifundefined{lemref}\n"
267                 "  {\\def\\RSlemtxt{lemma~}\\newref{lem}{name = \\RSlemtxt}}\n" 
268                 "  {}\n");
269
270
271 /////////////////////////////////////////////////////////////////////
272 //
273 // LaTeXFeatures
274 //
275 /////////////////////////////////////////////////////////////////////
276
277 LaTeXFeatures::Packages LaTeXFeatures::packages_;
278
279
280 LaTeXFeatures::LaTeXFeatures(Buffer const & b, BufferParams const & p,
281                              OutputParams const & r)
282         : buffer_(&b), params_(p), runparams_(r), in_float_(false)
283 {}
284
285
286 bool LaTeXFeatures::useBabel() const
287 {
288         return (lyxrc.language_package_selection != LyXRC::LP_NONE) &&
289                 ((bufferParams().language->lang() != lyxrc.default_language &&
290                  !bufferParams().language->babel().empty()) ||
291                 this->hasLanguages());
292 }
293
294
295 void LaTeXFeatures::require(string const & name)
296 {
297         features_.insert(name);
298 }
299
300
301 void LaTeXFeatures::require(set<string> const & names)
302 {
303         features_.insert(names.begin(), names.end());
304 }
305
306
307 void LaTeXFeatures::getAvailable()
308 {
309         Lexer lex;
310         support::FileName const real_file = libFileSearch("", "packages.lst");
311
312         if (real_file.empty())
313                 return;
314
315         lex.setFile(real_file);
316
317         if (!lex.isOK())
318                 return;
319
320         // Make sure that we are clean
321         packages_.clear();
322
323         bool finished = false;
324         // Parse config-file
325         while (lex.isOK() && !finished) {
326                 switch (lex.lex()) {
327                 case Lexer::LEX_FEOF:
328                         finished = true;
329                         break;
330                 default:
331                         packages_.insert(lex.getString());
332                 }
333         }
334 }
335
336
337 void LaTeXFeatures::useLayout(docstring const & layoutname)
338 {
339         // Some code to avoid loops in dependency definition
340         static int level = 0;
341         const int maxlevel = 30;
342         if (level > maxlevel) {
343                 lyxerr << "LaTeXFeatures::useLayout: maximum level of "
344                        << "recursion attained by layout "
345                        << to_utf8(layoutname) << endl;
346                 return;
347         }
348
349         DocumentClass const & tclass = params_.documentClass();
350         if (tclass.hasLayout(layoutname)) {
351                 // Is this layout already in usedLayouts?
352                 if (find(usedLayouts_.begin(), usedLayouts_.end(), layoutname) 
353                     != usedLayouts_.end())
354                         return;
355
356                 Layout const & layout = tclass[layoutname];
357                 require(layout.requires());
358
359                 if (!layout.depends_on().empty()) {
360                         ++level;
361                         useLayout(layout.depends_on());
362                         --level;
363                 }
364                 usedLayouts_.push_back(layoutname);
365         } else {
366                 lyxerr << "LaTeXFeatures::useLayout: layout `"
367                        << to_utf8(layoutname) << "' does not exist in this class"
368                        << endl;
369         }
370
371         --level;
372 }
373
374
375 void LaTeXFeatures::useInsetLayout(InsetLayout const & lay)
376 {
377         docstring const & lname = lay.name();
378         DocumentClass const & tclass = params_.documentClass();
379
380         // this is a default inset layout, nothing useful here
381         if (!tclass.hasInsetLayout(lname))
382                 return;
383         // Is this layout already in usedInsetLayouts?
384         if (find(usedInsetLayouts_.begin(), usedInsetLayouts_.end(), lname) 
385                         != usedInsetLayouts_.end())
386                 return;
387
388         require(lay.requires());
389         usedInsetLayouts_.push_back(lname);
390 }
391
392
393 bool LaTeXFeatures::isRequired(string const & name) const
394 {
395         return features_.find(name) != features_.end();
396 }
397
398
399 bool LaTeXFeatures::mustProvide(string const & name) const
400 {
401         return isRequired(name) && !params_.documentClass().provides(name);
402 }
403
404
405 bool LaTeXFeatures::isAvailable(string const & name)
406 {
407         string::size_type const i = name.find("->");
408         if (i != string::npos) {
409                 string const from = name.substr(0,i);
410                 string const to = name.substr(i+2);
411                 //LYXERR0("from=[" << from << "] to=[" << to << "]");
412                 return theConverters().isReachable(from, to);
413         }
414
415         if (packages_.empty())
416                 getAvailable();
417         string n = name;
418         if (suffixIs(n, ".sty"))
419                 n.erase(name.length() - 4);
420         return packages_.find(n) != packages_.end();
421 }
422
423
424 void LaTeXFeatures::addPreambleSnippet(string const & preamble)
425 {
426         SnippetList::const_iterator begin = preamble_snippets_.begin();
427         SnippetList::const_iterator end   = preamble_snippets_.end();
428         if (find(begin, end, preamble) == end)
429                 preamble_snippets_.push_back(preamble);
430 }
431
432
433 void LaTeXFeatures::useFloat(string const & name, bool subfloat)
434 {
435         if (!usedFloats_[name])
436                 usedFloats_[name] = subfloat;
437         if (subfloat)
438                 require("subfig");
439         // We only need float.sty if we use non builtin floats, or if we
440         // use the "H" modifier. This includes modified table and
441         // figure floats. (Lgb)
442         Floating const & fl = params_.documentClass().floats().getType(name);
443         if (!fl.floattype().empty() && fl.needsFloatPkg()) {
444                 require("float");
445         }
446 }
447
448
449 void LaTeXFeatures::useLanguage(Language const * lang)
450 {
451         if (!lang->babel().empty())
452                 UsedLanguages_.insert(lang);
453         if (lang->lang() == "vietnamese")
454                 require("vietnamese");
455         // CJK languages do not have a babel name.
456         // They use the CJK package
457         if (lang->encoding()->package() == Encoding::CJK)
458                 require("CJK");
459         // japanese package is special
460         if (lang->encoding()->package() == Encoding::japanese)
461                 require("japanese");
462 }
463
464
465 void LaTeXFeatures::includeFile(docstring const & key, string const & name)
466 {
467         IncludedFiles_[key] = name;
468 }
469
470
471 bool LaTeXFeatures::hasLanguages() const
472 {
473         return !UsedLanguages_.empty();
474 }
475
476
477 string LaTeXFeatures::getLanguages() const
478 {
479         ostringstream languages;
480
481         LanguageList::const_iterator const begin = UsedLanguages_.begin();
482         for (LanguageList::const_iterator cit = begin;
483              cit != UsedLanguages_.end();
484              ++cit) {
485                 if (cit != begin)
486                         languages << ',';
487                 languages << (*cit)->babel();
488         }
489         return languages.str();
490 }
491
492
493 set<string> LaTeXFeatures::getEncodingSet(string const & doc_encoding) const
494 {
495         // This does only find encodings of languages supported by babel, but
496         // that does not matter since we don't have a language with an
497         // encoding supported by inputenc but without babel support.
498         set<string> encodings;
499         LanguageList::const_iterator it  = UsedLanguages_.begin();
500         LanguageList::const_iterator end = UsedLanguages_.end();
501         for (; it != end; ++it)
502                 if ((*it)->encoding()->latexName() != doc_encoding &&
503                     ((*it)->encoding()->package() == Encoding::inputenc
504                      || (*it)->encoding()->package() == Encoding::japanese))
505                         encodings.insert((*it)->encoding()->latexName());
506         return encodings;
507 }
508
509 namespace {
510
511 char const * simplefeatures[] = {
512 // note that the package order here will be the same in the LaTeX-output
513         "array",
514         "verbatim",
515         "longtable",
516         "rotating",
517         "latexsym",
518         "pifont",
519         // subfig is handled in BufferParams.cpp
520         "varioref",
521         "prettyref",
522         "refstyle",
523         /*For a successful cooperation of the `wrapfig' package with the
524           `float' package you should load the `wrapfig' package *after*
525           the `float' package. See the caption package documentation
526           for explanation.*/
527         "float",
528         "rotfloat",
529         "wrapfig",
530         "booktabs",
531         "dvipost",
532         "fancybox",
533         "calc",
534         "units",
535         "tipa",
536         "tipx",
537         "framed",
538         "soul",
539         "textcomp",
540         "subscript",
541         "pmboxdraw",
542         "bbding",
543         "ifsym",
544         "marvosym",
545         "txfonts",
546         "mathrsfs",
547         "ascii",
548         "url",
549         "covington",
550         "csquotes",
551         "enumitem",
552         "endnotes",
553         "ifthen",
554         "amsthm",
555         // listings is handled in BufferParams.cpp
556         "bm",
557         "pdfpages",
558         "amscd",
559         "slashed",
560         "multirow",
561         "tfrupee"
562 };
563
564 int const nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
565
566 }
567
568
569 string const LaTeXFeatures::getColorOptions() const
570 {
571         ostringstream colors;
572
573         // Handling the color packages separately is needed to be able to load them
574         // before babel when hyperref is loaded with the colorlinks option
575         // for more info see Bufferparams.cpp
576
577         // [x]color.sty
578         if (mustProvide("color") || mustProvide("xcolor")) {
579                 string const package =
580                         (mustProvide("xcolor") ? "xcolor" : "color");
581                 if (params_.graphicsDriver == "default"
582                         || params_.graphicsDriver == "none")
583                         colors << "\\usepackage{" << package << "}\n";
584                 else
585                         colors << "\\usepackage["
586                                  << params_.graphicsDriver
587                                  << "]{" << package << "}\n";
588         }
589
590         // pdfcolmk must be loaded after color
591         if (mustProvide("pdfcolmk"))
592                 colors << "\\usepackage{pdfcolmk}\n";
593
594         // the following 3 color commands must be set after color
595         // is loaded and before pdfpages, therefore add the command
596         // here define the set color
597         if (mustProvide("pagecolor")) {
598                 colors << "\\definecolor{page_backgroundcolor}{rgb}{";
599                 colors << outputLaTeXColor(params_.backgroundcolor) << "}\n";
600                 // set the page color
601                 colors << "\\pagecolor{page_backgroundcolor}\n";
602         }
603
604         if (mustProvide("fontcolor")) {
605                 colors << "\\definecolor{document_fontcolor}{rgb}{";
606                 colors << outputLaTeXColor(params_.fontcolor) << "}\n";
607                 // set the color
608                 colors << "\\color{document_fontcolor}\n";
609         }
610
611         if (mustProvide("lyxgreyedout")) {
612                 colors << "\\definecolor{note_fontcolor}{rgb}{";
613                 colors << outputLaTeXColor(params_.notefontcolor) << "}\n";
614                 // the color will be set together with the definition of
615                 // the lyxgreyedout environment (see lyxgreyedout_def)
616         }
617
618         // color for shaded boxes
619         if (isRequired("framed") && mustProvide("color")) {
620                 colors << "\\definecolor{shadecolor}{rgb}{";
621                 colors << outputLaTeXColor(params_.boxbgcolor) << "}\n";
622                 // this color is automatically used by the LaTeX-package "framed"
623         }
624
625         return colors.str();
626 }
627
628
629 string const LaTeXFeatures::getPackages() const
630 {
631         ostringstream packages;
632         DocumentClass const & tclass = params_.documentClass();
633
634         // FIXME: currently, we can only load packages and macros known
635         // to LyX.
636         // However, with the Require tag of layouts/custom insets,
637         // also unknown packages can be requested. They are silently
638         // swallowed now. We should change this eventually.
639
640         //
641         //  These are all the 'simple' includes.  i.e
642         //  packages which we just \usepackage{package}
643         //
644         for (int i = 0; i < nb_simplefeatures; ++i) {
645                 if (mustProvide(simplefeatures[i]))
646                         packages << "\\usepackage{"
647                                  << simplefeatures[i] << "}\n";
648         }
649
650         //
651         // The rest of these packages are somewhat more complicated
652         // than those above.
653         //
654
655         // esint is preferred for esintoramsmath
656         if ((mustProvide("amsmath")
657              && params_.use_amsmath != BufferParams::package_off)
658             || (mustProvide("esintoramsmath")
659                 && params_.use_esint == BufferParams::package_off
660                 && params_.use_amsmath != BufferParams::package_off)) {
661                 packages << "\\usepackage{amsmath}\n";
662         } else {
663                 // amsbsy and amstext are already provided by amsmath
664                 if (mustProvide("amsbsy"))
665                         packages << "\\usepackage{amsbsy}\n";
666                 if (mustProvide("amstext"))
667                         packages << "\\usepackage{amstext}\n";
668         }
669         
670         // wasysym is a simple feature, but it must be after amsmath if both
671         // are used
672         // wasysym redefines some integrals (e.g. iint) from amsmath. That
673         // leads to inconsistent integrals. We only load this package if
674         // the document does not contain integrals (then isRequired("esint")
675         // is false) or if esint is used, since esint redefines all relevant
676         // integral symbols from wasysym and amsmath.
677         // See http://www.lyx.org/trac/ticket/1942
678         if (mustProvide("wasysym") &&
679             (params_.use_esint != BufferParams::package_off || !isRequired("esint")))
680                 packages << "\\usepackage{wasysym}\n";
681
682         // accents must be loaded after amsmath
683         if (mustProvide("accents"))
684                 packages << "\\usepackage{accents}\n";
685
686         // mathdots must be loaded after amsmath
687         if (mustProvide("mathdots") &&
688                 params_.use_mathdots != BufferParams::package_off)
689                 packages << "\\usepackage{mathdots}\n";
690
691         // yhmath must be loaded after amsmath
692         if (mustProvide("yhmath"))
693                 packages << "\\usepackage{yhmath}\n";
694
695         // [x]color and pdfcolmk are handled in getColorOptions() above
696         
697         // makeidx.sty
698         if (isRequired("makeidx") || isRequired("splitidx")) {
699                 if (!tclass.provides("makeidx") && !isRequired("splitidx"))
700                         packages << "\\usepackage{makeidx}\n";
701                 if (!tclass.provides("splitidx") && isRequired("splitidx"))
702                         packages << "\\usepackage{splitidx}\n";
703                 packages << "\\makeindex\n";
704         }
705
706         // graphicx.sty
707         if (mustProvide("graphicx") && params_.graphicsDriver != "none") {
708                 if (params_.graphicsDriver == "default")
709                         packages << "\\usepackage{graphicx}\n";
710                 else
711                         packages << "\\usepackage["
712                                  << params_.graphicsDriver
713                                  << "]{graphicx}\n";
714         }
715         
716         // lyxskak.sty --- newer chess support based on skak.sty
717         if (mustProvide("chess"))
718                 packages << "\\usepackage[ps,mover]{lyxskak}\n";
719
720         // setspace.sty
721         if (mustProvide("setspace") && !tclass.provides("SetSpace"))
722                 packages << "\\usepackage{setspace}\n";
723
724         // amssymb.sty
725         if (mustProvide("amssymb")
726             || params_.use_amsmath == BufferParams::package_on)
727                 packages << "\\usepackage{amssymb}\n";
728
729         // esint must be after amsmath and wasysym, since it will redeclare
730         // inconsistent integral symbols
731         if ((mustProvide("esint") || mustProvide("esintoramsmath")) &&
732             params_.use_esint != BufferParams::package_off)
733                 packages << "\\usepackage{esint}\n";
734
735         // natbib.sty
736         // Some classes load natbib themselves, but still allow (or even require)
737         // plain numeric citations (ReVTeX is such a case, see bug 5182).
738         // This special case is indicated by the "natbib-internal" key.
739         if (mustProvide("natbib") && !tclass.provides("natbib-internal")) {
740                 packages << "\\usepackage[";
741                 if (params_.citeEngine() == ENGINE_NATBIB_NUMERICAL)
742                         packages << "numbers";
743                 else
744                         packages << "authoryear";
745                 packages << "]{natbib}\n";
746         }
747
748         // jurabib -- we need version 0.6 at least.
749         if (mustProvide("jurabib"))
750                 packages << "\\usepackage{jurabib}[2004/01/25]\n";
751         
752         // xargs -- we need version 1.09 at least
753         if (mustProvide("xargs"))
754                 packages << "\\usepackage{xargs}[2008/03/08]\n";
755
756         // bibtopic -- the dot provides the aux file naming which
757         // LyX can detect.
758         if (mustProvide("bibtopic"))
759                 packages << "\\usepackage[dot]{bibtopic}\n";
760
761         if (mustProvide("xy"))
762                 packages << "\\usepackage[all]{xy}\n";
763
764         if (mustProvide("feyn"))
765                 packages << "\\usepackage{feyn}\n"; //Diagram
766
767         if (mustProvide("ulem"))
768                 packages << "\\PassOptionsToPackage{normalem}{ulem}\n"
769                             "\\usepackage{ulem}\n";
770
771         if (params_.use_mhchem == BufferParams::package_on ||
772             (mustProvide("mhchem") &&
773              params_.use_mhchem != BufferParams::package_off))
774                 packages << "\\PassOptionsToPackage{version=3}{mhchem}\n"
775                             "\\usepackage{mhchem}\n";
776
777         if (mustProvide("nomencl")) {
778                 // Make it work with the new and old version of the package,
779                 // but don't use the compatibility option since it is
780                 // incompatible to other packages.
781                 packages << "\\usepackage{nomencl}\n"
782                             "% the following is useful when we have the old nomencl.sty package\n"
783                             "\\providecommand{\\printnomenclature}{\\printglossary}\n"
784                             "\\providecommand{\\makenomenclature}{\\makeglossary}\n"
785                             "\\makenomenclature\n";
786         }
787
788         return packages.str();
789 }
790
791
792 string LaTeXFeatures::getPreambleSnippets() const 
793 {
794         ostringstream snip;
795         SnippetList::const_iterator pit  = preamble_snippets_.begin();
796         SnippetList::const_iterator pend = preamble_snippets_.end();
797         for (; pit != pend; ++pit)
798                 snip << *pit << '\n';
799         return snip.str();
800 }
801
802
803 docstring const LaTeXFeatures::getMacros() const
804 {
805         odocstringstream macros;
806
807         if (!preamble_snippets_.empty()) {
808                 macros << '\n';
809                 macros << from_utf8(getPreambleSnippets());
810         }
811
812         if (mustProvide("papersize")) {
813                 if (runparams_.flavor == OutputParams::LATEX)
814                         macros << papersizedvi_def << '\n';
815                 else
816                         macros << papersizepdf_def << '\n';
817         }
818
819         if (mustProvide("LyX"))
820                 macros << lyx_def << '\n';
821
822         if (mustProvide("noun"))
823                 macros << noun_def << '\n';
824
825         if (mustProvide("lyxarrow"))
826                 macros << lyxarrow_def << '\n';
827
828         if (mustProvide("textgreek")) {
829                 // Avoid a LaTeX error if times fonts are used and the grtimes
830                 // package is installed but actual fonts are not (bug 6469).
831                 if (params_.fontsRoman == "times")
832                         macros << subst(textgreek_def,
833                                         from_ascii("\\greektext #1"),
834                                         from_ascii("%\n  \\IfFileExists"
835                                                    "{grtm10.tfm}{}{\\fontfamily"
836                                                    "{cmr}}\\greektext #1"))
837                                << '\n';
838                 else
839                         macros << textgreek_def << '\n';
840         }
841
842         if (mustProvide("textcyr"))
843                 macros << textcyr_def << '\n';
844
845         if (mustProvide("lyxmathsym"))
846                 macros << lyxmathsym_def << '\n';
847
848         if (mustProvide("cedilla"))
849                 macros << cedilla_def << '\n';
850
851         if (mustProvide("subring"))
852                 macros << subring_def << '\n';
853
854         if (mustProvide("subdot"))
855                 macros << subdot_def << '\n';
856
857         if (mustProvide("subhat"))
858                 macros << subhat_def << '\n';
859
860         if (mustProvide("subtilde"))
861                 macros << subtilde_def << '\n';
862
863         if (mustProvide("dacute"))
864                 macros << dacute_def << '\n';
865
866         if (mustProvide("tipasymb"))
867                 macros << tipasymb_def << '\n';
868
869         if (mustProvide("dgrave"))
870                 macros << dgrave_def << '\n';
871
872         if (mustProvide("rcap"))
873                 macros << rcap_def << '\n';
874
875         if (mustProvide("ogonek"))
876                 macros << ogonek_def << '\n';
877
878         // quotes.
879         if (mustProvide("quotesinglbase"))
880                 macros << quotesinglbase_def << '\n';
881         if (mustProvide("quotedblbase"))
882                 macros << quotedblbase_def << '\n';
883         if (mustProvide("guilsinglleft"))
884                 macros << guilsinglleft_def << '\n';
885         if (mustProvide("guilsinglright"))
886                 macros << guilsinglright_def << '\n';
887         if (mustProvide("guillemotleft"))
888                 macros << guillemotleft_def << '\n';
889         if (mustProvide("guillemotright"))
890                 macros << guillemotright_def << '\n';
891
892         // Math mode
893         if (mustProvide("binom") && !isRequired("amsmath"))
894                 macros << binom_def << '\n';
895         if (mustProvide("mathcircumflex"))
896                 macros << mathcircumflex_def << '\n';
897
898         // other
899         if (mustProvide("ParagraphLeftIndent"))
900                 macros << paragraphleftindent_def;
901         if (mustProvide("NeedLyXFootnoteCode"))
902                 macros << floatingfootnote_def;
903
904         // some problems with tex->html converters
905         if (mustProvide("NeedTabularnewline"))
906                 macros << tabularnewline_def;
907
908         // greyed-out environment (note inset)
909         // the color is specified in the routine
910         // getColorOptions() to avoid LaTeX-package clashes
911         if (mustProvide("lyxgreyedout"))
912                 macros << lyxgreyedout_def;
913
914         if (mustProvide("lyxdot"))
915                 macros << lyxdot_def << '\n';
916
917         // floats
918         getFloatDefinitions(macros);
919         
920         if (mustProvide("refstyle")) 
921                 macros << lyxref_def << '\n';   
922         
923         // change tracking
924         if (mustProvide("ct-dvipost"))
925                 macros << changetracking_dvipost_def;
926         
927         if (mustProvide("ct-xcolor-ulem")) {
928                 streamsize const prec = macros.precision(2);
929         
930                 RGBColor cadd = rgbFromHexName(lcolor.getX11Name(Color_addedtext));
931                 macros << "\\providecolor{lyxadded}{rgb}{"
932                        << cadd.r / 255.0 << ',' << cadd.g / 255.0 << ',' << cadd.b / 255.0 << "}\n";
933
934                 RGBColor cdel = rgbFromHexName(lcolor.getX11Name(Color_deletedtext));
935                 macros << "\\providecolor{lyxdeleted}{rgb}{"
936                        << cdel.r / 255.0 << ',' << cdel.g / 255.0 << ',' << cdel.b / 255.0 << "}\n";
937
938                 macros.precision(prec);
939                 
940                 if (isRequired("hyperref"))
941                         macros << changetracking_xcolor_ulem_hyperref_def;
942                 else
943                         macros << changetracking_xcolor_ulem_def;
944         }
945
946         if (mustProvide("ct-none"))
947                 macros << changetracking_none_def;
948
949         return macros.str();
950 }
951
952
953 string const LaTeXFeatures::getBabelPresettings() const
954 {
955         ostringstream tmp;
956
957         LanguageList::const_iterator it  = UsedLanguages_.begin();
958         LanguageList::const_iterator end = UsedLanguages_.end();
959         for (; it != end; ++it)
960                 if (!(*it)->babel_presettings().empty())
961                         tmp << (*it)->babel_presettings() << '\n';
962         if (!params_.language->babel_presettings().empty())
963                 tmp << params_.language->babel_presettings() << '\n';
964
965         return tmp.str();
966 }
967
968
969 string const LaTeXFeatures::getBabelPostsettings() const
970 {
971         ostringstream tmp;
972
973         LanguageList::const_iterator it  = UsedLanguages_.begin();
974         LanguageList::const_iterator end = UsedLanguages_.end();
975         for (; it != end; ++it)
976                 if (!(*it)->babel_postsettings().empty())
977                         tmp << (*it)->babel_postsettings() << '\n';
978         if (!params_.language->babel_postsettings().empty())
979                 tmp << params_.language->babel_postsettings() << '\n';
980
981         return tmp.str();
982 }
983
984
985 bool LaTeXFeatures::needBabelLangOptions() const
986 {
987         if (!lyxrc.language_global_options || params_.language->asBabelOptions())
988                 return true;
989
990         LanguageList::const_iterator it  = UsedLanguages_.begin();
991         LanguageList::const_iterator end = UsedLanguages_.end();
992         for (; it != end; ++it)
993                 if ((*it)->asBabelOptions())
994                         return true;
995
996         return false;
997 }
998
999
1000 docstring const LaTeXFeatures::getTClassPreamble() const
1001 {
1002         // the text class specific preamble
1003         DocumentClass const & tclass = params_.documentClass();
1004         odocstringstream tcpreamble;
1005
1006         tcpreamble << tclass.preamble();
1007
1008         list<docstring>::const_iterator cit = usedLayouts_.begin();
1009         list<docstring>::const_iterator end = usedLayouts_.end();
1010         for (; cit != end; ++cit)
1011                 tcpreamble << tclass[*cit].preamble();
1012
1013         cit = usedInsetLayouts_.begin();
1014         end = usedInsetLayouts_.end();
1015         TextClass::InsetLayouts const & ils = tclass.insetLayouts();
1016         for (; cit != end; ++cit) {
1017                 TextClass::InsetLayouts::const_iterator it = ils.find(*cit);
1018                 if (it == ils.end())
1019                         continue;
1020                 tcpreamble << it->second.preamble();
1021         }
1022
1023         return tcpreamble.str();
1024 }
1025
1026
1027 docstring const LaTeXFeatures::getTClassHTMLPreamble() const 
1028 {
1029         DocumentClass const & tclass = params_.documentClass();
1030         odocstringstream tcpreamble;
1031
1032         tcpreamble << tclass.htmlpreamble();
1033
1034         list<docstring>::const_iterator cit = usedLayouts_.begin();
1035         list<docstring>::const_iterator end = usedLayouts_.end();
1036         for (; cit != end; ++cit)
1037                 tcpreamble << tclass[*cit].htmlpreamble();
1038
1039         cit = usedInsetLayouts_.begin();
1040         end = usedInsetLayouts_.end();
1041         TextClass::InsetLayouts const & ils = tclass.insetLayouts();
1042         for (; cit != end; ++cit) {
1043                 TextClass::InsetLayouts::const_iterator it = ils.find(*cit);
1044                 if (it == ils.end())
1045                         continue;
1046                 tcpreamble << it->second.htmlpreamble();
1047         }
1048
1049         return tcpreamble.str();
1050 }
1051
1052
1053 docstring const LaTeXFeatures::getTClassHTMLStyles() const {
1054         DocumentClass const & tclass = params_.documentClass();
1055         odocstringstream tcpreamble;
1056
1057         list<docstring>::const_iterator cit = usedLayouts_.begin();
1058         list<docstring>::const_iterator end = usedLayouts_.end();
1059         for (; cit != end; ++cit)
1060                 tcpreamble << tclass[*cit].htmlstyle();
1061
1062         cit = usedInsetLayouts_.begin();
1063         end = usedInsetLayouts_.end();
1064         TextClass::InsetLayouts const & ils = tclass.insetLayouts();
1065         for (; cit != end; ++cit) {
1066                 TextClass::InsetLayouts::const_iterator it = ils.find(*cit);
1067                 if (it == ils.end())
1068                         continue;
1069                 tcpreamble << it->second.htmlstyle();
1070         }
1071
1072         return tcpreamble.str();
1073 }
1074
1075
1076 namespace {
1077 docstring const getFloatI18nPreamble(docstring const & type, docstring const & name, docstring const & lang)
1078 {
1079         odocstringstream os;
1080         os << "\\addto\\captions" << lang
1081            << "{\\renewcommand{\\" << type << "name}{" << name << "}}\n";
1082         return os.str();
1083 }
1084 }
1085
1086
1087 docstring const LaTeXFeatures::getTClassI18nPreamble(bool use_babel) const
1088 {
1089         DocumentClass const & tclass = params_.documentClass();
1090         // collect preamble snippets in a set to prevent multiple identical
1091         // commands (would happen if e.g. both theorem and theorem* are used)
1092         set<docstring> snippets;
1093         typedef LanguageList::const_iterator lang_it;
1094         lang_it const lbeg = UsedLanguages_.begin();
1095         lang_it const lend =  UsedLanguages_.end();
1096         list<docstring>::const_iterator cit = usedLayouts_.begin();
1097         list<docstring>::const_iterator end = usedLayouts_.end();
1098         for (; cit != end; ++cit) {
1099                 // language dependent commands (once per document)
1100                 snippets.insert(tclass[*cit].langpreamble(buffer().language()));
1101                 // commands for language changing (for multilanguage documents)
1102                 if (use_babel && !UsedLanguages_.empty()) {
1103                         snippets.insert(tclass[*cit].babelpreamble(buffer().language()));
1104                         for (lang_it lit = lbeg; lit != lend; ++lit)
1105                                 snippets.insert(tclass[*cit].babelpreamble(*lit));
1106                 }
1107         }
1108         if (use_babel && !UsedLanguages_.empty()) {
1109                 FloatList const & floats = params_.documentClass().floats();
1110                 UsedFloats::const_iterator fit = usedFloats_.begin();
1111                 UsedFloats::const_iterator fend = usedFloats_.end();
1112                 for (; fit != fend; ++fit) {
1113                         Floating const & fl = floats.getType(fit->first);
1114                         docstring const type = from_ascii(fl.floattype());
1115                         docstring const flname = from_utf8(fl.name());
1116                         docstring name = translateIfPossible(flname,
1117                                 buffer().language()->code());
1118                         snippets.insert(getFloatI18nPreamble(
1119                                 type, name,
1120                                 from_ascii(buffer().language()->babel())));
1121                         for (lang_it lit = lbeg; lit != lend; ++lit) {
1122                                 name = translateIfPossible(flname,
1123                                         (*lit)->code());
1124                                 snippets.insert(getFloatI18nPreamble(
1125                                         type, name,
1126                                         from_ascii((*lit)->babel())));
1127                         }
1128                 }
1129         }
1130
1131         odocstringstream tcpreamble;
1132         set<docstring>::const_iterator const send = snippets.end();
1133         set<docstring>::const_iterator it = snippets.begin();
1134         for (; it != send; ++it)
1135                 tcpreamble << *it;
1136         return tcpreamble.str();
1137 }
1138
1139
1140 docstring const LaTeXFeatures::getLyXSGMLEntities() const
1141 {
1142         // Definition of entities used in the document that are LyX related.
1143         odocstringstream entities;
1144
1145         if (mustProvide("lyxarrow")) {
1146                 entities << "<!ENTITY lyxarrow \"-&gt;\">" << '\n';
1147         }
1148
1149         return entities.str();
1150 }
1151
1152
1153 docstring const LaTeXFeatures::getIncludedFiles(string const & fname) const
1154 {
1155         odocstringstream sgmlpreamble;
1156         // FIXME UNICODE
1157         docstring const basename(from_utf8(onlyPath(fname)));
1158
1159         FileMap::const_iterator end = IncludedFiles_.end();
1160         for (FileMap::const_iterator fi = IncludedFiles_.begin();
1161              fi != end; ++fi)
1162                 // FIXME UNICODE
1163                 sgmlpreamble << "\n<!ENTITY " << fi->first
1164                              << (isSGMLFileName(fi->second) ? " SYSTEM \"" : " \"")
1165                              << makeRelPath(from_utf8(fi->second), basename) << "\">";
1166
1167         return sgmlpreamble.str();
1168 }
1169
1170
1171 void LaTeXFeatures::showStruct() const
1172 {
1173         lyxerr << "LyX needs the following commands when LaTeXing:"
1174                << "\n***** Packages:" << getPackages()
1175                << "\n***** Macros:" << to_utf8(getMacros())
1176                << "\n***** Textclass stuff:" << to_utf8(getTClassPreamble())
1177                << "\n***** done." << endl;
1178 }
1179
1180
1181 Buffer const & LaTeXFeatures::buffer() const
1182 {
1183         return *buffer_;
1184 }
1185
1186
1187 void LaTeXFeatures::setBuffer(Buffer const & buffer)
1188 {
1189         buffer_ = &buffer;
1190 }
1191
1192
1193 BufferParams const & LaTeXFeatures::bufferParams() const
1194 {
1195         return params_;
1196 }
1197
1198
1199 void LaTeXFeatures::getFloatDefinitions(odocstream & os) const
1200 {
1201         FloatList const & floats = params_.documentClass().floats();
1202
1203         // Here we will output the code to create the needed float styles.
1204         // We will try to do this as minimal as possible.
1205         // \floatstyle{ruled}
1206         // \newfloat{algorithm}{htbp}{loa}
1207         // \providecommand{\algorithmname}{Algorithm}
1208         // \floatname{algorithm}{\protect\algorithmname}
1209         UsedFloats::const_iterator cit = usedFloats_.begin();
1210         UsedFloats::const_iterator end = usedFloats_.end();
1211         for (; cit != end; ++cit) {
1212                 Floating const & fl = floats.getType(cit->first);
1213
1214                 // For builtin floats we do nothing.
1215                 if (!fl.needsFloatPkg()) 
1216                         continue;
1217
1218                 // We have to special case "table" and "figure"
1219                 if (fl.floattype() == "tabular" || fl.floattype() == "figure") {
1220                         // Output code to modify "table" or "figure"
1221                         // but only if builtin == false
1222                         // and that have to be true at this point in the
1223                         // function.
1224                         docstring const type = from_ascii(fl.floattype());
1225                         docstring const placement = from_ascii(fl.placement());
1226                         docstring const style = from_ascii(fl.style());
1227                         if (!style.empty()) {
1228                                 os << "\\floatstyle{" << style << "}\n"
1229                                    << "\\restylefloat{" << type << "}\n";
1230                         }
1231                         if (!placement.empty()) {
1232                                 os << "\\floatplacement{" << type << "}{"
1233                                    << placement << "}\n";
1234                         }
1235                 } else {
1236                         // The other non builtin floats.
1237
1238                         docstring const type = from_ascii(fl.floattype());
1239                         docstring const placement = from_ascii(fl.placement());
1240                         docstring const ext = from_ascii(fl.ext());
1241                         docstring const within = from_ascii(fl.within());
1242                         docstring const style = from_ascii(fl.style());
1243                         docstring const name = translateIfPossible(
1244                                         from_utf8(fl.name()),
1245                                         buffer().language()->code());
1246                         os << "\\floatstyle{" << style << "}\n"
1247                            << "\\newfloat{" << type << "}{" << placement
1248                            << "}{" << ext << '}';
1249                         if (!within.empty())
1250                                 os << '[' << within << ']';
1251                         os << '\n'
1252                            << "\\providecommand{\\" << type << "name}{"
1253                            << name << "}\n"
1254                            << "\\floatname{" << type << "}{\\protect\\"
1255                            << type << "name}\n";
1256
1257                         // What missing here is to code to minimalize the code
1258                         // output so that the same floatstyle will not be
1259                         // used several times, when the same style is still in
1260                         // effect. (Lgb)
1261                 }
1262                 if (cit->second)
1263                         os << "\n\\newsubfloat{" << from_ascii(fl.floattype()) << "}\n";
1264         }
1265 }
1266
1267
1268 } // namespace lyx