]> git.lyx.org Git - lyx.git/blob - src/BufferParams.cpp
More work towards type safety regarding TextClass's. A couple bugs have been fixed...
[lyx.git] / src / BufferParams.cpp
1 /**
2  * \file BufferParams.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author John Levon
10  * \author André Pönitz
11  * \author Martin Vermeer
12  *
13  * Full author contact details are available in file CREDITS.
14  */
15
16 #include <config.h>
17
18 #include "BufferParams.h"
19
20 #include "Author.h"
21 #include "BaseClassList.h"
22 #include "BranchList.h"
23 #include "buffer_funcs.h"
24 #include "Bullet.h"
25 #include "Color.h"
26 #include "Encoding.h"
27 #include "Language.h"
28 #include "LaTeXFeatures.h"
29 #include "ModuleList.h"
30 #include "Font.h"
31 #include "Lexer.h"
32 #include "LyXRC.h"
33 #include "OutputParams.h"
34 #include "Spacing.h"
35 #include "TexRow.h"
36 #include "VSpace.h"
37 #include "PDFOptions.h"
38
39 #include "frontends/alert.h"
40
41 #include "insets/InsetListingsParams.h"
42
43 #include "support/convert.h"
44 #include "support/debug.h"
45 #include "support/docstream.h"
46 #include "support/FileName.h"
47 #include "support/filetools.h"
48 #include "support/gettext.h"
49 #include "support/Messages.h"
50 #include "support/Translator.h"
51 #include "support/lstrings.h"
52
53 #include <algorithm>
54 #include <sstream>
55
56 using namespace std;
57 using namespace lyx::support;
58
59
60 static char const * const string_paragraph_separation[] = {
61         "indent", "skip", ""
62 };
63
64
65 static char const * const string_quotes_language[] = {
66         "english", "swedish", "german", "polish", "french", "danish", ""
67 };
68
69
70 static char const * const string_papersize[] = {
71         "default", "custom", "letterpaper", "executivepaper", "legalpaper",
72         "a3paper", "a4paper", "a5paper", "b3paper", "b4paper", "b5paper", ""
73 };
74
75
76 static char const * const string_orientation[] = {
77         "portrait", "landscape", ""
78 };
79
80
81 static char const * const string_footnotekinds[] = {
82         "footnote", "margin", "fig", "tab", "alg", "wide-fig", "wide-tab", ""
83 };
84
85
86 static char const * const tex_graphics[] = {
87         "default", "dvips", "dvitops", "emtex",
88         "ln", "oztex", "textures", "none", ""
89 };
90
91
92 namespace lyx {
93
94 // Local translators
95 namespace {
96
97 // Paragraph separation
98 typedef Translator<string, BufferParams::PARSEP> ParSepTranslator;
99
100
101 ParSepTranslator const init_parseptranslator()
102 {
103         ParSepTranslator translator(string_paragraph_separation[0], BufferParams::PARSEP_INDENT);
104         translator.addPair(string_paragraph_separation[1], BufferParams::PARSEP_SKIP);
105         return translator;
106 }
107
108
109 ParSepTranslator const & parseptranslator()
110 {
111         static ParSepTranslator translator = init_parseptranslator();
112         return translator;
113 }
114
115
116 // Quotes language
117 typedef Translator<string, InsetQuotes::quote_language> QuotesLangTranslator;
118
119
120 QuotesLangTranslator const init_quoteslangtranslator()
121 {
122         QuotesLangTranslator translator(string_quotes_language[0], InsetQuotes::EnglishQ);
123         translator.addPair(string_quotes_language[1], InsetQuotes::SwedishQ);
124         translator.addPair(string_quotes_language[2], InsetQuotes::GermanQ);
125         translator.addPair(string_quotes_language[3], InsetQuotes::PolishQ);
126         translator.addPair(string_quotes_language[4], InsetQuotes::FrenchQ);
127         translator.addPair(string_quotes_language[5], InsetQuotes::DanishQ);
128         return translator;
129 }
130
131
132 QuotesLangTranslator const & quoteslangtranslator()
133 {
134         static QuotesLangTranslator translator = init_quoteslangtranslator();
135         return translator;
136 }
137
138
139 // Paper size
140 typedef Translator<string, PAPER_SIZE> PaperSizeTranslator;
141
142
143 PaperSizeTranslator const init_papersizetranslator()
144 {
145         PaperSizeTranslator translator(string_papersize[0], PAPER_DEFAULT);
146         translator.addPair(string_papersize[1], PAPER_CUSTOM);
147         translator.addPair(string_papersize[2], PAPER_USLETTER);
148         translator.addPair(string_papersize[3], PAPER_USLEGAL);
149         translator.addPair(string_papersize[4], PAPER_USEXECUTIVE);
150         translator.addPair(string_papersize[5], PAPER_A3);
151         translator.addPair(string_papersize[6], PAPER_A4);
152         translator.addPair(string_papersize[7], PAPER_A5);
153         translator.addPair(string_papersize[8], PAPER_B3);
154         translator.addPair(string_papersize[9], PAPER_B4);
155         translator.addPair(string_papersize[10], PAPER_B5);
156         return translator;
157 }
158
159
160 PaperSizeTranslator const & papersizetranslator()
161 {
162         static PaperSizeTranslator translator = init_papersizetranslator();
163         return translator;
164 }
165
166
167 // Paper orientation
168 typedef Translator<string, PAPER_ORIENTATION> PaperOrientationTranslator;
169
170
171 PaperOrientationTranslator const init_paperorientationtranslator()
172 {
173         PaperOrientationTranslator translator(string_orientation[0], ORIENTATION_PORTRAIT);
174         translator.addPair(string_orientation[1], ORIENTATION_LANDSCAPE);
175         return translator;
176 }
177
178
179 PaperOrientationTranslator const & paperorientationtranslator()
180 {
181         static PaperOrientationTranslator translator = init_paperorientationtranslator();
182         return translator;
183 }
184
185
186 // Page sides
187 typedef Translator<int, PageSides> SidesTranslator;
188
189
190 SidesTranslator const init_sidestranslator()
191 {
192         SidesTranslator translator(1, OneSide);
193         translator.addPair(2, TwoSides);
194         return translator;
195 }
196
197
198 SidesTranslator const & sidestranslator()
199 {
200         static SidesTranslator translator = init_sidestranslator();
201         return translator;
202 }
203
204
205 // LaTeX packages
206 typedef Translator<int, BufferParams::Package> PackageTranslator;
207
208
209 PackageTranslator const init_packagetranslator()
210 {
211         PackageTranslator translator(0, BufferParams::package_off);
212         translator.addPair(1, BufferParams::package_auto);
213         translator.addPair(2, BufferParams::package_on);
214         return translator;
215 }
216
217
218 PackageTranslator const & packagetranslator()
219 {
220         static PackageTranslator translator = init_packagetranslator();
221         return translator;
222 }
223
224
225 // Cite engine
226 typedef Translator<string, biblio::CiteEngine> CiteEngineTranslator;
227
228
229 CiteEngineTranslator const init_citeenginetranslator()
230 {
231         CiteEngineTranslator translator("basic", biblio::ENGINE_BASIC);
232         translator.addPair("natbib_numerical", biblio::ENGINE_NATBIB_NUMERICAL);
233         translator.addPair("natbib_authoryear", biblio::ENGINE_NATBIB_AUTHORYEAR);
234         translator.addPair("jurabib", biblio::ENGINE_JURABIB);
235         return translator;
236 }
237
238
239 CiteEngineTranslator const & citeenginetranslator()
240 {
241         static CiteEngineTranslator translator = init_citeenginetranslator();
242         return translator;
243 }
244
245
246 // Spacing
247 typedef Translator<string, Spacing::Space> SpaceTranslator;
248
249
250 SpaceTranslator const init_spacetranslator()
251 {
252         SpaceTranslator translator("default", Spacing::Default);
253         translator.addPair("single", Spacing::Single);
254         translator.addPair("onehalf", Spacing::Onehalf);
255         translator.addPair("double", Spacing::Double);
256         translator.addPair("other", Spacing::Other);
257         return translator;
258 }
259
260
261 SpaceTranslator const & spacetranslator()
262 {
263         static SpaceTranslator translator = init_spacetranslator();
264         return translator;
265 }
266
267
268 } // anon namespace
269
270
271 class BufferParams::Impl
272 {
273 public:
274         Impl();
275
276         AuthorList authorlist;
277         BranchList branchlist;
278         Bullet temp_bullets[4];
279         Bullet user_defined_bullets[4];
280         Spacing spacing;
281         /** This is the amount of space used for paragraph_separation "skip",
282          * and for detached paragraphs in "indented" documents.
283          */
284         VSpace defskip;
285         PDFOptions pdfoptions;
286         BaseClassIndex baseClass_;
287 };
288
289
290 BufferParams::Impl::Impl()
291         : defskip(VSpace::MEDSKIP), baseClass_(string(""))
292 {
293         // set initial author
294         // FIXME UNICODE
295         authorlist.record(Author(from_utf8(lyxrc.user_name), from_utf8(lyxrc.user_email)));
296 }
297
298
299 BufferParams::Impl *
300 BufferParams::MemoryTraits::clone(BufferParams::Impl const * ptr)
301 {
302         BOOST_ASSERT(ptr);
303
304         return new BufferParams::Impl(*ptr);
305 }
306
307
308 void BufferParams::MemoryTraits::destroy(BufferParams::Impl * ptr)
309 {
310         delete ptr;
311 }
312
313
314 BufferParams::BufferParams()
315         : pimpl_(new Impl)
316 {
317         setBaseClass(defaultBaseclass());
318         makeDocumentClass();
319         paragraph_separation = PARSEP_INDENT;
320         quotes_language = InsetQuotes::EnglishQ;
321         fontsize = "default";
322
323         /*  PaperLayout */
324         papersize = PAPER_DEFAULT;
325         orientation = ORIENTATION_PORTRAIT;
326         use_geometry = false;
327         use_amsmath = package_auto;
328         use_esint = package_auto;
329         cite_engine_ = biblio::ENGINE_BASIC;
330         use_bibtopic = false;
331         trackChanges = false;
332         outputChanges = false;
333         secnumdepth = 3;
334         tocdepth = 3;
335         language = default_language;
336         fontsRoman = "default";
337         fontsSans = "default";
338         fontsTypewriter = "default";
339         fontsDefaultFamily = "default";
340         fontsSC = false;
341         fontsOSF = false;
342         fontsSansScale = 100;
343         fontsTypewriterScale = 100;
344         inputenc = "auto";
345         graphicsDriver = "default";
346         sides = OneSide;
347         columns = 1;
348         listings_params = string();
349         pagestyle = "default";
350         compressed = false;
351         embedded = false;
352         for (int iter = 0; iter < 4; ++iter) {
353                 user_defined_bullet(iter) = ITEMIZE_DEFAULTS[iter];
354                 temp_bullet(iter) = ITEMIZE_DEFAULTS[iter];
355         }
356 }
357
358
359 BufferParams::~BufferParams()
360 {}
361
362
363 docstring const BufferParams::B_(string const & l10n) const
364 {
365         BOOST_ASSERT(language);
366         return getMessages(language->code()).get(l10n);
367 }
368
369
370 AuthorList & BufferParams::authors()
371 {
372         return pimpl_->authorlist;
373 }
374
375
376 AuthorList const & BufferParams::authors() const
377 {
378         return pimpl_->authorlist;
379 }
380
381
382 BranchList & BufferParams::branchlist()
383 {
384         return pimpl_->branchlist;
385 }
386
387
388 BranchList const & BufferParams::branchlist() const
389 {
390         return pimpl_->branchlist;
391 }
392
393
394 Bullet & BufferParams::temp_bullet(lyx::size_type const index)
395 {
396         BOOST_ASSERT(index < 4);
397         return pimpl_->temp_bullets[index];
398 }
399
400
401 Bullet const & BufferParams::temp_bullet(lyx::size_type const index) const
402 {
403         BOOST_ASSERT(index < 4);
404         return pimpl_->temp_bullets[index];
405 }
406
407
408 Bullet & BufferParams::user_defined_bullet(lyx::size_type const index)
409 {
410         BOOST_ASSERT(index < 4);
411         return pimpl_->user_defined_bullets[index];
412 }
413
414
415 Bullet const & BufferParams::user_defined_bullet(lyx::size_type const index) const
416 {
417         BOOST_ASSERT(index < 4);
418         return pimpl_->user_defined_bullets[index];
419 }
420
421
422 Spacing & BufferParams::spacing()
423 {
424         return pimpl_->spacing;
425 }
426
427
428 Spacing const & BufferParams::spacing() const
429 {
430         return pimpl_->spacing;
431 }
432
433
434 PDFOptions & BufferParams::pdfoptions()
435 {
436         return pimpl_->pdfoptions;
437 }
438
439
440 PDFOptions const & BufferParams::pdfoptions() const
441 {
442         return pimpl_->pdfoptions;
443 }
444
445
446 VSpace const & BufferParams::getDefSkip() const
447 {
448         return pimpl_->defskip;
449 }
450
451
452 void BufferParams::setDefSkip(VSpace const & vs)
453 {
454         pimpl_->defskip = vs;
455 }
456
457
458 string const BufferParams::readToken(Lexer & lex, string const & token,
459         FileName const & filepath)
460 {
461         if (token == "\\textclass") {
462                 lex.next();
463                 string const classname = lex.getString();
464                 // if there exists a local layout file, ignore the system one
465                 // NOTE: in this case, the textclass (.cls file) is assumed to be available.
466                 string tcp;
467                 BaseClassList & bcl = BaseClassList::get();
468                 if (!filepath.empty())
469                         tcp = bcl.addTextClass(classname, filepath.absFilename());
470                 if (!tcp.empty())
471                         setBaseClass(tcp);
472                 else if (bcl.haveClass(classname)) {
473                         setBaseClass(classname);
474                 } else {
475                         // a warning will be given for unknown class
476                         setBaseClass(defaultBaseclass());
477                         return classname;
478                 }
479                 // FIXME: this warning will be given even if there exists a local .cls
480                 // file. Even worse, the .lyx file can not be compiled or exported
481                 // because the textclass is marked as unavilable.
482                 if (!documentClass().isTeXClassAvailable()) {
483                         docstring const msg =
484                                 bformat(_("The layout file requested by this document,\n"
485                                                  "%1$s.layout,\n"
486                                                  "is not usable. This is probably because a LaTeX\n"
487                                                  "class or style file required by it is not\n"
488                                                  "available. See the Customization documentation\n"
489                                                  "for more information.\n"), from_utf8(classname));
490                         frontend::Alert::warning(_("Document class not available"),
491                                        msg + _("LyX will not be able to produce output."));
492                 } 
493                 
494         } else if (token == "\\begin_preamble") {
495                 readPreamble(lex);
496         } else if (token == "\\begin_modules") {
497                 readModules(lex);
498         } else if (token == "\\options") {
499                 lex.eatLine();
500                 options = lex.getString();
501         } else if (token == "\\language") {
502                 readLanguage(lex);
503         } else if (token == "\\inputencoding") {
504                 lex >> inputenc;
505         } else if (token == "\\graphics") {
506                 readGraphicsDriver(lex);
507         } else if (token == "\\font_roman") {
508                 lex >> fontsRoman;
509         } else if (token == "\\font_sans") {
510                 lex >> fontsSans;
511         } else if (token == "\\font_typewriter") {
512                 lex >> fontsTypewriter;
513         } else if (token == "\\font_default_family") {
514                 lex >> fontsDefaultFamily;
515         } else if (token == "\\font_sc") {
516                 lex >> fontsSC;
517         } else if (token == "\\font_osf") {
518                 lex >> fontsOSF;
519         } else if (token == "\\font_sf_scale") {
520                 lex >> fontsSansScale;
521         } else if (token == "\\font_tt_scale") {
522                 lex >> fontsTypewriterScale;
523         } else if (token == "\\paragraph_separation") {
524                 string parsep;
525                 lex >> parsep;
526                 paragraph_separation = parseptranslator().find(parsep);
527         } else if (token == "\\defskip") {
528                 lex.next();
529                 string defskip = lex.getString();
530                 if (defskip == "defskip")
531                         // this is invalid
532                         defskip = "medskip";
533                 pimpl_->defskip = VSpace(defskip);
534         } else if (token == "\\quotes_language") {
535                 string quotes_lang;
536                 lex >> quotes_lang;
537                 quotes_language = quoteslangtranslator().find(quotes_lang);
538         } else if (token == "\\papersize") {
539                 string ppsize;
540                 lex >> ppsize;
541                 papersize = papersizetranslator().find(ppsize);
542         } else if (token == "\\use_geometry") {
543                 lex >> use_geometry;
544         } else if (token == "\\use_amsmath") {
545                 int use_ams;
546                 lex >> use_ams;
547                 use_amsmath = packagetranslator().find(use_ams);
548         } else if (token == "\\use_esint") {
549                 int useesint;
550                 lex >> useesint;
551                 use_esint = packagetranslator().find(useesint);
552         } else if (token == "\\cite_engine") {
553                 string engine;
554                 lex >> engine;
555                 cite_engine_ = citeenginetranslator().find(engine);
556         } else if (token == "\\use_bibtopic") {
557                 lex >> use_bibtopic;
558         } else if (token == "\\tracking_changes") {
559                 lex >> trackChanges;
560         } else if (token == "\\output_changes") {
561                 lex >> outputChanges;
562         } else if (token == "\\branch") {
563                 lex.next();
564                 docstring branch = lex.getDocString();
565                 branchlist().add(branch);
566                 while (true) {
567                         lex.next();
568                         string const tok = lex.getString();
569                         if (tok == "\\end_branch")
570                                 break;
571                         Branch * branch_ptr = branchlist().find(branch);
572                         if (tok == "\\selected") {
573                                 lex.next();
574                                 if (branch_ptr)
575                                         branch_ptr->setSelected(lex.getInteger());
576                         }
577                         // not yet operational
578                         if (tok == "\\color") {
579                                 lex.eatLine();
580                                 string color = lex.getString();
581                                 if (branch_ptr)
582                                         branch_ptr->setColor(color);
583                                 // Update also the Color table:
584                                 if (color == "none")
585                                         color = lcolor.getX11Name(Color_background);
586                                 // FIXME UNICODE
587                                 lcolor.setColor(to_utf8(branch), color);
588
589                         }
590                 }
591         } else if (token == "\\author") {
592                 lex.eatLine();
593                 istringstream ss(lex.getString());
594                 Author a;
595                 ss >> a;
596                 author_map.push_back(pimpl_->authorlist.record(a));
597         } else if (token == "\\paperorientation") {
598                 string orient;
599                 lex >> orient;
600                 orientation = paperorientationtranslator().find(orient);
601         } else if (token == "\\paperwidth") {
602                 lex >> paperwidth;
603         } else if (token == "\\paperheight") {
604                 lex >> paperheight;
605         } else if (token == "\\leftmargin") {
606                 lex >> leftmargin;
607         } else if (token == "\\topmargin") {
608                 lex >> topmargin;
609         } else if (token == "\\rightmargin") {
610                 lex >> rightmargin;
611         } else if (token == "\\bottommargin") {
612                 lex >> bottommargin;
613         } else if (token == "\\headheight") {
614                 lex >> headheight;
615         } else if (token == "\\headsep") {
616                 lex >> headsep;
617         } else if (token == "\\footskip") {
618                 lex >> footskip;
619         } else if (token == "\\columnsep") {
620                 lex >> columnsep;
621         } else if (token == "\\paperfontsize") {
622                 lex >> fontsize;
623         } else if (token == "\\papercolumns") {
624                 lex >> columns;
625         } else if (token == "\\listings_params") {
626                 string par;
627                 lex >> par;
628                 listings_params = InsetListingsParams(par).params();
629         } else if (token == "\\papersides") {
630                 int psides;
631                 lex >> psides;
632                 sides = sidestranslator().find(psides);
633         } else if (token == "\\paperpagestyle") {
634                 lex >> pagestyle;
635         } else if (token == "\\bullet") {
636                 readBullets(lex);
637         } else if (token == "\\bulletLaTeX") {
638                 readBulletsLaTeX(lex);
639         } else if (token == "\\secnumdepth") {
640                 lex >> secnumdepth;
641         } else if (token == "\\tocdepth") {
642                 lex >> tocdepth;
643         } else if (token == "\\spacing") {
644                 string nspacing;
645                 lex >> nspacing;
646                 string tmp_val;
647                 if (nspacing == "other") {
648                         lex >> tmp_val;
649                 }
650                 spacing().set(spacetranslator().find(nspacing), tmp_val);
651         } else if (token == "\\float_placement") {
652                 lex >> float_placement;
653
654         } else if (prefixIs(token, "\\pdf_") || token == "\\use_hyperref") {
655                 string toktmp = pdfoptions().readToken(lex, token);
656                 if (!toktmp.empty()) {
657                         lyxerr << "PDFOptions::readToken(): Unknown token: " <<
658                                 toktmp << endl;
659                         return toktmp;
660                 }
661         } else {
662                 lyxerr << "BufferParams::readToken(): Unknown token: " << 
663                         token << endl;
664                 return token;
665         }
666
667         return string();
668 }
669
670
671 void BufferParams::writeFile(ostream & os) const
672 {
673         // The top of the file is written by the buffer.
674         // Prints out the buffer info into the .lyx file given by file
675
676         // the textclass
677         os << "\\textclass " << baseClass()->name() << '\n';
678
679         // then the preamble
680         if (!preamble.empty()) {
681                 // remove '\n' from the end of preamble
682                 string const tmppreamble = rtrim(preamble, "\n");
683                 os << "\\begin_preamble\n"
684                    << tmppreamble
685                    << "\n\\end_preamble\n";
686         }
687
688         // the options
689         if (!options.empty()) {
690                 os << "\\options " << options << '\n';
691         }
692         
693         //the modules
694         if (!layoutModules_.empty()) {
695                 os << "\\begin_modules" << '\n';
696                 LayoutModuleList::const_iterator it = layoutModules_.begin();
697                 for (; it != layoutModules_.end(); it++)
698                         os << *it << '\n';
699                 os << "\\end_modules" << '\n';
700         }
701
702         // then the text parameters
703         if (language != ignore_language)
704                 os << "\\language " << language->lang() << '\n';
705         os << "\\inputencoding " << inputenc
706            << "\n\\font_roman " << fontsRoman
707            << "\n\\font_sans " << fontsSans
708            << "\n\\font_typewriter " << fontsTypewriter
709            << "\n\\font_default_family " << fontsDefaultFamily
710            << "\n\\font_sc " << convert<string>(fontsSC)
711            << "\n\\font_osf " << convert<string>(fontsOSF)
712            << "\n\\font_sf_scale " << fontsSansScale
713            << "\n\\font_tt_scale " << fontsTypewriterScale
714            << "\n\\graphics " << graphicsDriver << '\n';
715
716         if (!float_placement.empty()) {
717                 os << "\\float_placement " << float_placement << '\n';
718         }
719         os << "\\paperfontsize " << fontsize << '\n';
720
721         spacing().writeFile(os);
722         pdfoptions().writeFile(os);
723
724         os << "\\papersize " << string_papersize[papersize]
725            << "\n\\use_geometry " << convert<string>(use_geometry)
726            << "\n\\use_amsmath " << use_amsmath
727            << "\n\\use_esint " << use_esint
728            << "\n\\cite_engine " << citeenginetranslator().find(cite_engine_)
729            << "\n\\use_bibtopic " << convert<string>(use_bibtopic)
730            << "\n\\paperorientation " << string_orientation[orientation]
731            << '\n';
732
733         BranchList::const_iterator it = branchlist().begin();
734         BranchList::const_iterator end = branchlist().end();
735         for (; it != end; ++it) {
736                 os << "\\branch " << to_utf8(it->getBranch())
737                    << "\n\\selected " << it->getSelected()
738                    << "\n\\color " << lyx::X11hexname(it->getColor())
739                    << "\n\\end_branch"
740                    << "\n";
741         }
742
743         if (!paperwidth.empty())
744                 os << "\\paperwidth "
745                    << VSpace(paperwidth).asLyXCommand() << '\n';
746         if (!paperheight.empty())
747                 os << "\\paperheight "
748                    << VSpace(paperheight).asLyXCommand() << '\n';
749         if (!leftmargin.empty())
750                 os << "\\leftmargin "
751                    << VSpace(leftmargin).asLyXCommand() << '\n';
752         if (!topmargin.empty())
753                 os << "\\topmargin "
754                    << VSpace(topmargin).asLyXCommand() << '\n';
755         if (!rightmargin.empty())
756                 os << "\\rightmargin "
757                    << VSpace(rightmargin).asLyXCommand() << '\n';
758         if (!bottommargin.empty())
759                 os << "\\bottommargin "
760                    << VSpace(bottommargin).asLyXCommand() << '\n';
761         if (!headheight.empty())
762                 os << "\\headheight "
763                    << VSpace(headheight).asLyXCommand() << '\n';
764         if (!headsep.empty())
765                 os << "\\headsep "
766                    << VSpace(headsep).asLyXCommand() << '\n';
767         if (!footskip.empty())
768                 os << "\\footskip "
769                    << VSpace(footskip).asLyXCommand() << '\n';
770         if (!columnsep.empty())
771                 os << "\\columnsep " 
772                          << VSpace(columnsep).asLyXCommand() << '\n';
773         os << "\\secnumdepth " << secnumdepth
774            << "\n\\tocdepth " << tocdepth
775            << "\n\\paragraph_separation "
776            << string_paragraph_separation[paragraph_separation]
777            << "\n\\defskip " << getDefSkip().asLyXCommand()
778            << "\n\\quotes_language "
779            << string_quotes_language[quotes_language]
780            << "\n\\papercolumns " << columns
781            << "\n\\papersides " << sides
782            << "\n\\paperpagestyle " << pagestyle << '\n';
783         if (!listings_params.empty())
784                 os << "\\listings_params \"" <<
785                         InsetListingsParams(listings_params).encodedString() << "\"\n";
786         for (int i = 0; i < 4; ++i) {
787                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
788                         if (user_defined_bullet(i).getFont() != -1) {
789                                 os << "\\bullet " << i << " "
790                                    << user_defined_bullet(i).getFont() << " "
791                                    << user_defined_bullet(i).getCharacter() << " "
792                                    << user_defined_bullet(i).getSize() << "\n";
793                         }
794                         else {
795                                 // FIXME UNICODE
796                                 os << "\\bulletLaTeX " << i << " \""
797                                    << lyx::to_ascii(user_defined_bullet(i).getText())
798                                    << "\"\n";
799                         }
800                 }
801         }
802
803         os << "\\tracking_changes " << convert<string>(trackChanges) << "\n";
804         os << "\\output_changes " << convert<string>(outputChanges) << "\n";
805
806         AuthorList::Authors::const_iterator a_it = pimpl_->authorlist.begin();
807         AuthorList::Authors::const_iterator a_end = pimpl_->authorlist.end();
808         for (; a_it != a_end; ++a_it) {
809                 if (a_it->second.used())
810                         os << "\\author " << a_it->second << "\n";
811                 else
812                         os << "\\author " << Author() << "\n";
813         }
814 }
815
816
817 void BufferParams::validate(LaTeXFeatures & features) const
818 {
819         features.require(documentClass().requires());
820
821         if (outputChanges) {
822                 bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
823                 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
824                                   LaTeXFeatures::isAvailable("xcolor");
825
826                 switch (features.runparams().flavor) {
827                 case OutputParams::LATEX:
828                         if (dvipost) {
829                                 features.require("ct-dvipost");
830                                 features.require("dvipost");
831                         } else if (xcolorsoul) {
832                                 features.require("ct-xcolor-soul");
833                                 features.require("soul");
834                                 features.require("xcolor");
835                         } else {
836                                 features.require("ct-none");
837                         }
838                         break;
839                 case OutputParams::PDFLATEX:
840                         if (xcolorsoul) {
841                                 features.require("ct-xcolor-soul");
842                                 features.require("soul");
843                                 features.require("xcolor");
844                                 // improves color handling in PDF output
845                                 features.require("pdfcolmk"); 
846                         } else {
847                                 features.require("ct-none");
848                         }
849                         break;
850                 default:
851                         break;
852                 }
853         }
854
855         // Floats with 'Here definitely' as default setting.
856         if (float_placement.find('H') != string::npos)
857                 features.require("float");
858
859         // AMS Style is at document level
860         if (use_amsmath == package_on
861             || documentClass().provides("amsmath"))
862                 features.require("amsmath");
863         if (use_esint == package_on)
864                 features.require("esint");
865
866         // Document-level line spacing
867         if (spacing().getSpace() != Spacing::Single && !spacing().isDefault())
868                 features.require("setspace");
869
870         // the bullet shapes are buffer level not paragraph level
871         // so they are tested here
872         for (int i = 0; i < 4; ++i) {
873                 if (user_defined_bullet(i) == ITEMIZE_DEFAULTS[i]) 
874                         continue;
875                 int const font = user_defined_bullet(i).getFont();
876                 if (font == 0) {
877                         int const c = user_defined_bullet(i).getCharacter();
878                         if (c == 16
879                             || c == 17
880                             || c == 25
881                             || c == 26
882                             || c == 31) {
883                                 features.require("latexsym");
884                         }
885                 } else if (font == 1) {
886                         features.require("amssymb");
887                 } else if (font >= 2 && font <= 5) {
888                         features.require("pifont");
889                 }
890         }
891
892         if (pdfoptions().use_hyperref)
893                 features.require("hyperref");
894 }
895
896
897 bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features,
898                               TexRow & texrow) const
899 {
900         os << "\\documentclass";
901
902         DocumentClass const & tclass = documentClass();
903
904         ostringstream clsoptions; // the document class options.
905
906         if (tokenPos(tclass.opt_fontsize(),
907                      '|', fontsize) >= 0) {
908                 // only write if existing in list (and not default)
909                 clsoptions << fontsize << "pt,";
910         }
911
912         // custom, A3, B3 and B4 paper sizes need geometry
913         bool nonstandard_papersize = papersize == PAPER_B3
914                 || papersize == PAPER_B4
915                 || papersize == PAPER_A3
916                 || papersize == PAPER_CUSTOM;
917
918         if (!use_geometry) {
919                 switch (papersize) {
920                 case PAPER_A4:
921                         clsoptions << "a4paper,";
922                         break;
923                 case PAPER_USLETTER:
924                         clsoptions << "letterpaper,";
925                         break;
926                 case PAPER_A5:
927                         clsoptions << "a5paper,";
928                         break;
929                 case PAPER_B5:
930                         clsoptions << "b5paper,";
931                         break;
932                 case PAPER_USEXECUTIVE:
933                         clsoptions << "executivepaper,";
934                         break;
935                 case PAPER_USLEGAL:
936                         clsoptions << "legalpaper,";
937                         break;
938                 case PAPER_DEFAULT:
939                 case PAPER_A3:
940                 case PAPER_B3:
941                 case PAPER_B4:
942                 case PAPER_CUSTOM:
943                         break;
944                 }
945         }
946
947         // if needed
948         if (sides != tclass.sides()) {
949                 switch (sides) {
950                 case OneSide:
951                         clsoptions << "oneside,";
952                         break;
953                 case TwoSides:
954                         clsoptions << "twoside,";
955                         break;
956                 }
957         }
958
959         // if needed
960         if (columns != tclass.columns()) {
961                 if (columns == 2)
962                         clsoptions << "twocolumn,";
963                 else
964                         clsoptions << "onecolumn,";
965         }
966
967         if (!use_geometry
968             && orientation == ORIENTATION_LANDSCAPE)
969                 clsoptions << "landscape,";
970
971         // language should be a parameter to \documentclass
972         if (language->babel() == "hebrew"
973             && default_language->babel() != "hebrew")
974                 // This seems necessary
975                 features.useLanguage(default_language);
976
977         ostringstream language_options;
978         bool const use_babel = features.useBabel();
979         if (use_babel) {
980                 language_options << features.getLanguages();
981                 if (!language->babel().empty()) {
982                         if (!language_options.str().empty())
983                                 language_options << ',';
984                         language_options << language->babel();
985                 }
986                 // when Vietnamese is used, babel must directly be loaded with the
987                 // language options, not in the class options, see
988                 // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129417.html
989                 size_t viet = language_options.str().find("vietnam");
990                 // viet = string::npos when not found
991                 if (lyxrc.language_global_options && !language_options.str().empty()
992                         && viet == string::npos)
993                         clsoptions << language_options.str() << ',';
994         }
995
996         // the user-defined options
997         if (!options.empty()) {
998                 clsoptions << options << ',';
999         }
1000
1001         string strOptions(clsoptions.str());
1002         if (!strOptions.empty()) {
1003                 strOptions = rtrim(strOptions, ",");
1004                 // FIXME UNICODE
1005                 os << '[' << from_utf8(strOptions) << ']';
1006         }
1007
1008         os << '{' << from_ascii(tclass.latexname()) << "}\n";
1009         texrow.newline();
1010         // end of \documentclass defs
1011
1012         // font selection must be done before loading fontenc.sty
1013         string const fonts =
1014                 loadFonts(fontsRoman, fontsSans,
1015                           fontsTypewriter, fontsSC, fontsOSF,
1016                           fontsSansScale, fontsTypewriterScale);
1017         if (!fonts.empty()) {
1018                 os << from_ascii(fonts);
1019                 texrow.newline();
1020         }
1021         if (fontsDefaultFamily != "default")
1022                 os << "\\renewcommand{\\familydefault}{\\"
1023                    << from_ascii(fontsDefaultFamily) << "}\n";
1024
1025         // set font encoding
1026         // this one is not per buffer
1027         // for arabic_arabi and farsi we also need to load the LAE and LFE encoding
1028         if (lyxrc.fontenc != "default") {
1029                 if (language->lang() == "arabic_arabi" || language->lang() == "farsi") {
1030                         os << "\\usepackage[" << from_ascii(lyxrc.fontenc)
1031                            << ",LFE,LAE]{fontenc}\n";
1032                         texrow.newline();
1033                 } else {
1034                         os << "\\usepackage[" << from_ascii(lyxrc.fontenc)
1035                            << "]{fontenc}\n";
1036                         texrow.newline();
1037                 }
1038         }
1039
1040         // handle inputenc etc.
1041         writeEncodingPreamble(os, features, texrow);
1042
1043         if (!listings_params.empty()) {
1044                 os << "\\usepackage{listings}\n";
1045                 texrow.newline();
1046                 os << "\\lstset{";
1047                 // do not test validity because listings_params is supposed to be valid
1048                 string par = InsetListingsParams(listings_params).separatedParams(true);
1049                 os << from_ascii(par);
1050                 // count the number of newlines
1051                 for (size_t i = 0; i < par.size(); ++i)
1052                         if (par[i] == '\n')
1053                                 texrow.newline();
1054                 os << "}\n";
1055                 texrow.newline();
1056         }
1057         if (use_geometry || nonstandard_papersize) {
1058                 os << "\\usepackage{geometry}\n";
1059                 texrow.newline();
1060                 os << "\\geometry{verbose";
1061                 if (orientation == ORIENTATION_LANDSCAPE)
1062                         os << ",landscape";
1063                 switch (papersize) {
1064                 case PAPER_CUSTOM:
1065                         if (!paperwidth.empty())
1066                                 os << ",paperwidth="
1067                                    << from_ascii(paperwidth);
1068                         if (!paperheight.empty())
1069                                 os << ",paperheight="
1070                                    << from_ascii(paperheight);
1071                         break;
1072                 case PAPER_USLETTER:
1073                         os << ",letterpaper";
1074                         break;
1075                 case PAPER_USLEGAL:
1076                         os << ",legalpaper";
1077                         break;
1078                 case PAPER_USEXECUTIVE:
1079                         os << ",executivepaper";
1080                         break;
1081                 case PAPER_A3:
1082                         os << ",a3paper";
1083                         break;
1084                 case PAPER_A4:
1085                         os << ",a4paper";
1086                         break;
1087                 case PAPER_A5:
1088                         os << ",a5paper";
1089                         break;
1090                 case PAPER_B3:
1091                         os << ",b3paper";
1092                         break;
1093                 case PAPER_B4:
1094                         os << ",b4paper";
1095                         break;
1096                 case PAPER_B5:
1097                         os << ",b5paper";
1098                         break;
1099                 default:
1100                         // default papersize ie PAPER_DEFAULT
1101                         switch (lyxrc.default_papersize) {
1102                         case PAPER_DEFAULT: // keep compiler happy
1103                         case PAPER_USLETTER:
1104                                 os << ",letterpaper";
1105                                 break;
1106                         case PAPER_USLEGAL:
1107                                 os << ",legalpaper";
1108                                 break;
1109                         case PAPER_USEXECUTIVE:
1110                                 os << ",executivepaper";
1111                                 break;
1112                         case PAPER_A3:
1113                                 os << ",a3paper";
1114                                 break;
1115                         case PAPER_A4:
1116                                 os << ",a4paper";
1117                                 break;
1118                         case PAPER_A5:
1119                                 os << ",a5paper";
1120                                 break;
1121                         case PAPER_B5:
1122                                 os << ",b5paper";
1123                                 break;
1124                         case PAPER_B3:
1125                         case PAPER_B4:
1126                         case PAPER_CUSTOM:
1127                                 break;
1128                         }
1129                 }
1130                 if (!topmargin.empty())
1131                         os << ",tmargin=" << from_ascii(Length(topmargin).asLatexString());
1132                 if (!bottommargin.empty())
1133                         os << ",bmargin=" << from_ascii(Length(bottommargin).asLatexString());
1134                 if (!leftmargin.empty())
1135                         os << ",lmargin=" << from_ascii(Length(leftmargin).asLatexString());
1136                 if (!rightmargin.empty())
1137                         os << ",rmargin=" << from_ascii(Length(rightmargin).asLatexString());
1138                 if (!headheight.empty())
1139                         os << ",headheight=" << from_ascii(Length(headheight).asLatexString());
1140                 if (!headsep.empty())
1141                         os << ",headsep=" << from_ascii(Length(headsep).asLatexString());
1142                 if (!footskip.empty())
1143                         os << ",footskip=" << from_ascii(Length(footskip).asLatexString());
1144                 if (!columnsep.empty())
1145                         os << ",columnsep=" << from_ascii(Length(columnsep).asLatexString());
1146                 os << "}\n";
1147                 texrow.newline();
1148         }
1149
1150         if (tokenPos(tclass.opt_pagestyle(),
1151                      '|', pagestyle) >= 0) {
1152                 if (pagestyle == "fancy") {
1153                         os << "\\usepackage{fancyhdr}\n";
1154                         texrow.newline();
1155                 }
1156                 os << "\\pagestyle{" << from_ascii(pagestyle) << "}\n";
1157                 texrow.newline();
1158         }
1159
1160         // Only if class has a ToC hierarchy
1161         if (tclass.hasTocLevels()) {
1162                 if (secnumdepth != tclass.secnumdepth()) {
1163                         os << "\\setcounter{secnumdepth}{"
1164                            << secnumdepth
1165                            << "}\n";
1166                         texrow.newline();
1167                 }
1168                 if (tocdepth != tclass.tocdepth()) {
1169                         os << "\\setcounter{tocdepth}{"
1170                            << tocdepth
1171                            << "}\n";
1172                         texrow.newline();
1173                 }
1174         }
1175
1176         if (paragraph_separation) {
1177                 switch (getDefSkip().kind()) {
1178                 case VSpace::SMALLSKIP:
1179                         os << "\\setlength{\\parskip}{\\smallskipamount}\n";
1180                         break;
1181                 case VSpace::MEDSKIP:
1182                         os << "\\setlength{\\parskip}{\\medskipamount}\n";
1183                         break;
1184                 case VSpace::BIGSKIP:
1185                         os << "\\setlength{\\parskip}{\\bigskipamount}\n";
1186                         break;
1187                 case VSpace::LENGTH:
1188                         os << "\\setlength{\\parskip}{"
1189                            << from_utf8(getDefSkip().length().asLatexString())
1190                            << "}\n";
1191                         break;
1192                 default: // should never happen // Then delete it.
1193                         os << "\\setlength{\\parskip}{\\medskipamount}\n";
1194                         break;
1195                 }
1196                 texrow.newline();
1197
1198                 os << "\\setlength{\\parindent}{0pt}\n";
1199                 texrow.newline();
1200         }
1201
1202         // If we use jurabib, we have to call babel here.
1203         if (use_babel && features.isRequired("jurabib")) {
1204                 os << from_ascii(babelCall(language_options.str()))
1205                    << '\n'
1206                    << from_ascii(features.getBabelOptions());
1207                 texrow.newline();
1208         }
1209
1210         // Now insert the LyX specific LaTeX commands...
1211
1212         // The optional packages;
1213         docstring lyxpreamble(from_ascii(features.getPackages()));
1214
1215         // Line spacing
1216         lyxpreamble += from_utf8(spacing().writePreamble(tclass.provides("SetSpace")));
1217
1218         // We try to load babel late, in case it interferes
1219         // with other packages. But some packages also need babel to be loaded
1220         // before, e.g. jurabib has to be called after babel.
1221         // So load babel after the optional packages but before the user-defined
1222         // preamble. This allows the users to redefine babel commands, e.g. to
1223         // translate the word "Index" to the German "Stichwortverzeichnis".
1224         // For more infos why this place was chosen, see
1225         // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg128425.html
1226         // If you encounter problems, you can shift babel to its old place behind
1227         // the user-defined preamble. But in this case you must change the Vietnamese
1228         // support from currently "\usepackage[vietnamese]{babel}" to:
1229         // \usepackage{vietnamese}
1230         // \usepackage{babel}
1231         // because vietnamese must be loaded before hyperref
1232         if (use_babel && !features.isRequired("jurabib")) {
1233                 // FIXME UNICODE
1234                 lyxpreamble += from_utf8(babelCall(language_options.str())) + '\n';
1235                 lyxpreamble += from_utf8(features.getBabelOptions());
1236         }
1237
1238         // When the language "japanese-plain" is used, the package "japanese" must
1239         // be loaded behind babel (it provides babel support for Japanese) but before
1240         // hyperref, see
1241         // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html
1242         if (language->lang() == "japanese-plain" &&
1243                 !documentClass().provides("japanese")) {
1244                 //load babel in case it was not loaded due to an empty language list
1245                 if (language_options.str().empty())
1246                         lyxpreamble += "\\usepackage{babel}\n";
1247                 lyxpreamble += "\\usepackage{japanese}\n";
1248         }
1249
1250         // PDF support.
1251         // * Hyperref manual: "Make sure it comes last of your loaded
1252         //   packages, to give it a fighting chance of not being over-written,
1253         //   since its job is to redefine many LATEX commands."
1254         // * Email from Heiko Oberdiek: "It is usually better to load babel
1255         //   before hyperref. Then hyperref has a chance to detect babel.
1256         // * Has to be loaded before the "LyX specific LaTeX commands" to
1257         //   avoid errors with algorithm floats.
1258         // use hyperref explicitely when it is required
1259         if (features.isRequired("hyperref")) {
1260                 odocstringstream oss;
1261                 pdfoptions().writeLaTeX(oss, documentClass().provides("hyperref"));
1262                 lyxpreamble += oss.str();
1263         }
1264
1265         // this might be useful...
1266         lyxpreamble += "\n\\makeatletter\n";
1267
1268         // Some macros LyX will need
1269         docstring tmppreamble(from_ascii(features.getMacros()));
1270
1271         if (!tmppreamble.empty()) {
1272                 lyxpreamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1273                         "LyX specific LaTeX commands.\n"
1274                         + tmppreamble + '\n';
1275         }
1276
1277         // the text class specific preamble
1278         tmppreamble = features.getTClassPreamble();
1279         if (!tmppreamble.empty()) {
1280                 lyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1281                         "Textclass specific LaTeX commands.\n"
1282                         + tmppreamble + '\n';
1283         }
1284
1285         /* the user-defined preamble */
1286         if (!preamble.empty()) {
1287                 // FIXME UNICODE
1288                 lyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1289                         "User specified LaTeX commands.\n"
1290                         + from_utf8(preamble) + '\n';
1291         }
1292
1293         // Itemize bullet settings need to be last in case the user
1294         // defines their own bullets that use a package included
1295         // in the user-defined preamble -- ARRae
1296         // Actually it has to be done much later than that
1297         // since some packages like frenchb make modifications
1298         // at \begin{document} time -- JMarc
1299         docstring bullets_def;
1300         for (int i = 0; i < 4; ++i) {
1301                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1302                         if (bullets_def.empty())
1303                                 bullets_def += "\\AtBeginDocument{\n";
1304                         bullets_def += "  \\def\\labelitemi";
1305                         switch (i) {
1306                                 // `i' is one less than the item to modify
1307                         case 0:
1308                                 break;
1309                         case 1:
1310                                 bullets_def += 'i';
1311                                 break;
1312                         case 2:
1313                                 bullets_def += "ii";
1314                                 break;
1315                         case 3:
1316                                 bullets_def += 'v';
1317                                 break;
1318                         }
1319                         bullets_def += '{' +
1320                                 user_defined_bullet(i).getText()
1321                                 + "}\n";
1322                 }
1323         }
1324
1325         if (!bullets_def.empty())
1326                 lyxpreamble += bullets_def + "}\n\n";
1327
1328         lyxpreamble += "\\makeatother\n\n";
1329
1330         int const nlines =
1331                 int(count(lyxpreamble.begin(), lyxpreamble.end(), '\n'));
1332         for (int j = 0; j != nlines; ++j) {
1333                 texrow.newline();
1334         }
1335
1336         os << lyxpreamble;
1337         return use_babel;
1338 }
1339
1340
1341 void BufferParams::useClassDefaults()
1342 {
1343         DocumentClass const & tclass = documentClass();
1344
1345         sides = tclass.sides();
1346         columns = tclass.columns();
1347         pagestyle = tclass.pagestyle();
1348         options = tclass.options();
1349         // Only if class has a ToC hierarchy
1350         if (tclass.hasTocLevels()) {
1351                 secnumdepth = tclass.secnumdepth();
1352                 tocdepth = tclass.tocdepth();
1353         }
1354 }
1355
1356
1357 bool BufferParams::hasClassDefaults() const
1358 {
1359         DocumentClass const & tclass = documentClass();
1360
1361         return sides == tclass.sides()
1362                 && columns == tclass.columns()
1363                 && pagestyle == tclass.pagestyle()
1364                 && options == tclass.options()
1365                 && secnumdepth == tclass.secnumdepth()
1366                 && tocdepth == tclass.tocdepth();
1367 }
1368
1369
1370 DocumentClass const & BufferParams::documentClass() const
1371 {
1372         return *doc_class_;
1373 }
1374
1375
1376 DocumentClass * BufferParams::documentClassPtr() const {
1377         return doc_class_;
1378 }
1379
1380
1381 void BufferParams::setDocumentClass(DocumentClass const * const tc) {
1382         // evil, but this function is evil
1383         doc_class_ = const_cast<DocumentClass *>(tc);
1384 }
1385
1386
1387 bool BufferParams::setBaseClass(string const & classname)
1388 {
1389         string localtc = classname;
1390         BaseClassList const & bcl = BaseClassList::get();
1391         if (!bcl.haveClass(localtc)) {
1392                 // OK, let's try again assuming it's a local file
1393                 localtc = BaseClassList::localPrefix + localtc;
1394                 if (!bcl.haveClass(localtc)) {
1395                         docstring s = 
1396                                 bformat(_("The document class %1$s could not be found."),
1397                                 from_utf8(classname));
1398                         frontend::Alert::error(_("Class not found"), s);
1399                         return false;
1400                 }
1401         }
1402
1403         if (bcl[localtc].load()) {
1404                 pimpl_->baseClass_ = localtc;
1405                 return true;
1406         }
1407         
1408         docstring s = 
1409                 bformat(_("The document class %1$s could not be loaded."),
1410                 from_utf8(classname));
1411         frontend::Alert::error(_("Could not load class"), s);
1412         return false;
1413 }
1414
1415
1416 TextClass const * BufferParams::baseClass() const
1417 {
1418         if (BaseClassList::get().haveClass(pimpl_->baseClass_))
1419                 return &(BaseClassList::get()[pimpl_->baseClass_]);
1420         else 
1421                 return 0;
1422 }
1423
1424
1425 BaseClassIndex const & BufferParams::baseClassID() const
1426 {
1427         return pimpl_->baseClass_;
1428 }
1429
1430
1431 void BufferParams::makeDocumentClass()
1432 {
1433         if (!baseClass())
1434                 return;
1435
1436         doc_class_ = &(DocumentClassBundle::get().newClass(*baseClass()));
1437         
1438         //FIXME It might be worth loading the children's modules here,
1439         //just as we load their bibliographies and such, instead of just 
1440         //doing a check in InsetInclude.
1441         LayoutModuleList::const_iterator it = layoutModules_.begin();
1442         for (; it != layoutModules_.end(); it++) {
1443                 string const modName = *it;
1444                 LyXModule * lm = moduleList[modName];
1445                 if (!lm) {
1446                         docstring const msg =
1447                                 bformat(_("The module %1$s has been requested by\n"
1448                                         "this document but has not been found in the list of\n"
1449                                         "available modules. If you recently installed it, you\n"
1450                                         "probably need to reconfigure LyX.\n"), from_utf8(modName));
1451                         frontend::Alert::warning(_("Module not available"),
1452                                         msg + _("Some layouts may not be available."));
1453                         lyxerr << "BufferParams::makeDocumentClass(): Module " <<
1454                                         modName << " requested but not found in module list." <<
1455                                         endl;
1456                         continue;
1457                 }
1458                 if (!lm->isAvailable()) {
1459                         docstring const msg =
1460                                                 bformat(_("The module %1$s requires a package that is\n"
1461                                                 "not available in your LaTeX installation. LaTeX output\n"
1462                                                 "may not be possible.\n"), from_utf8(modName));
1463                         frontend::Alert::warning(_("Package not available"), msg);
1464                 }
1465                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1466                 if (!doc_class_->read(layout_file, TextClass::MODULE)) {
1467                         docstring const msg =
1468                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1469                         frontend::Alert::warning(_("Read Error"), msg);
1470                 }
1471         }
1472 }
1473
1474
1475 vector<string> const & BufferParams::getModules() const 
1476 {
1477         return layoutModules_;
1478 }
1479
1480
1481
1482 bool BufferParams::addLayoutModule(string const & modName) 
1483 {
1484         LayoutModuleList::const_iterator it = layoutModules_.begin();
1485         LayoutModuleList::const_iterator end = layoutModules_.end();
1486         for (; it != end; it++) {
1487                 if (*it == modName) 
1488                         break;
1489         }
1490         if (it != layoutModules_.end())
1491                 return false;
1492         layoutModules_.push_back(modName);
1493         return true;
1494 }
1495
1496
1497 void BufferParams::clearLayoutModules() 
1498 {
1499         layoutModules_.clear();
1500 }
1501
1502
1503 Font const BufferParams::getFont() const
1504 {
1505         FontInfo f = documentClass().defaultfont();
1506         if (fontsDefaultFamily == "rmdefault")
1507                 f.setFamily(ROMAN_FAMILY);
1508         else if (fontsDefaultFamily == "sfdefault")
1509                 f.setFamily(SANS_FAMILY);
1510         else if (fontsDefaultFamily == "ttdefault")
1511                 f.setFamily(TYPEWRITER_FAMILY);
1512         return Font(f, language);
1513 }
1514
1515
1516 void BufferParams::readPreamble(Lexer & lex)
1517 {
1518         if (lex.getString() != "\\begin_preamble")
1519                 lyxerr << "Error (BufferParams::readPreamble):"
1520                         "consistency check failed." << endl;
1521
1522         preamble = lex.getLongString("\\end_preamble");
1523 }
1524
1525
1526 void BufferParams::readLanguage(Lexer & lex)
1527 {
1528         if (!lex.next()) return;
1529
1530         string const tmptok = lex.getString();
1531
1532         // check if tmptok is part of tex_babel in tex-defs.h
1533         language = languages.getLanguage(tmptok);
1534         if (!language) {
1535                 // Language tmptok was not found
1536                 language = default_language;
1537                 lyxerr << "Warning: Setting language `"
1538                        << tmptok << "' to `" << language->lang()
1539                        << "'." << endl;
1540         }
1541 }
1542
1543
1544 void BufferParams::readGraphicsDriver(Lexer & lex)
1545 {
1546         if (!lex.next()) 
1547                 return;
1548
1549         string const tmptok = lex.getString();
1550         // check if tmptok is part of tex_graphics in tex_defs.h
1551         int n = 0;
1552         while (true) {
1553                 string const test = tex_graphics[n++];
1554
1555                 if (test == tmptok) {
1556                         graphicsDriver = tmptok;
1557                         break;
1558                 } else if (test == "") {
1559                         lex.printError(
1560                                 "Warning: graphics driver `$$Token' not recognized!\n"
1561                                 "         Setting graphics driver to `default'.\n");
1562                         graphicsDriver = "default";
1563                         break;
1564                 }
1565         }
1566 }
1567
1568
1569 void BufferParams::readBullets(Lexer & lex)
1570 {
1571         if (!lex.next()) 
1572                 return;
1573
1574         int const index = lex.getInteger();
1575         lex.next();
1576         int temp_int = lex.getInteger();
1577         user_defined_bullet(index).setFont(temp_int);
1578         temp_bullet(index).setFont(temp_int);
1579         lex >> temp_int;
1580         user_defined_bullet(index).setCharacter(temp_int);
1581         temp_bullet(index).setCharacter(temp_int);
1582         lex >> temp_int;
1583         user_defined_bullet(index).setSize(temp_int);
1584         temp_bullet(index).setSize(temp_int);
1585 }
1586
1587
1588 void BufferParams::readBulletsLaTeX(Lexer & lex)
1589 {
1590         // The bullet class should be able to read this.
1591         if (!lex.next()) 
1592                 return;
1593         int const index = lex.getInteger();
1594         lex.next(true);
1595         docstring const temp_str = lex.getDocString();
1596
1597         user_defined_bullet(index).setText(temp_str);
1598         temp_bullet(index).setText(temp_str);
1599 }
1600
1601
1602 void BufferParams::readModules(Lexer & lex)
1603 {
1604         if (!lex.eatLine()) {
1605                 lyxerr << "Error (BufferParams::readModules):"
1606                                 "Unexpected end of input." << endl;
1607                 return;
1608         }
1609         while (true) {
1610                 string mod = lex.getString();
1611                 if (mod == "\\end_modules")
1612                         break;
1613                 addLayoutModule(mod);
1614                 lex.eatLine();
1615         }
1616 }
1617
1618
1619 string const BufferParams::paperSizeName(Papersize_Purpose const & purpose) const
1620 {
1621         char real_papersize = papersize;
1622         if (real_papersize == PAPER_DEFAULT)
1623                 real_papersize = lyxrc.default_papersize;
1624
1625         switch (real_papersize) {
1626         case PAPER_DEFAULT:
1627                 // could be anything, so don't guess
1628                 return string();
1629         case PAPER_CUSTOM: {
1630                 if (purpose == XDVI && !paperwidth.empty() &&
1631                     !paperheight.empty()) {
1632                         // heightxwidth<unit>
1633                         string first = paperwidth;
1634                         string second = paperheight;
1635                         if (orientation == ORIENTATION_LANDSCAPE)
1636                                 first.swap(second);
1637                         // cut off unit.
1638                         return first.erase(first.length() - 2)
1639                                 + "x" + second;
1640                 }
1641                 return string();
1642         }
1643         case PAPER_A3:
1644                 return "a3";
1645         case PAPER_A4:
1646                 return "a4";
1647         case PAPER_A5:
1648                 return "a5";
1649         case PAPER_B3:
1650                 // dvips and dvipdfm do not know this
1651                 if (purpose == DVIPS || purpose == DVIPDFM)
1652                         return string();
1653                 return "b3";
1654         case PAPER_B4:
1655                 // dvipdfm does not know this
1656                 if (purpose == DVIPDFM)
1657                         return string();
1658                 return "b4";
1659         case PAPER_B5:
1660                 // dvipdfm does not know this
1661                 if (purpose == DVIPDFM)
1662                         return string();
1663                 return "b5";
1664         case PAPER_USEXECUTIVE:
1665                 // dvipdfm does not know this
1666                 if (purpose == DVIPDFM)
1667                         return string();
1668                 return "foolscap";
1669         case PAPER_USLEGAL:
1670                 return "legal";
1671         case PAPER_USLETTER:
1672         default:
1673                 if (purpose == XDVI)
1674                         return "us";
1675                 return "letter";
1676         }
1677 }
1678
1679
1680 string const BufferParams::dvips_options() const
1681 {
1682         string result;
1683
1684         if (use_geometry
1685             && papersize == PAPER_CUSTOM
1686             && !lyxrc.print_paper_dimension_flag.empty()
1687             && !paperwidth.empty()
1688             && !paperheight.empty()) {
1689                 // using a custom papersize
1690                 result = lyxrc.print_paper_dimension_flag;
1691                 result += ' ' + paperwidth;
1692                 result += ',' + paperheight;
1693         } else {
1694                 string const paper_option = paperSizeName(DVIPS);
1695                 if (!paper_option.empty() && (paper_option != "letter" ||
1696                     orientation != ORIENTATION_LANDSCAPE)) {
1697                         // dvips won't accept -t letter -t landscape.
1698                         // In all other cases, include the paper size
1699                         // explicitly.
1700                         result = lyxrc.print_paper_flag;
1701                         result += ' ' + paper_option;
1702                 }
1703         }
1704         if (orientation == ORIENTATION_LANDSCAPE &&
1705             papersize != PAPER_CUSTOM)
1706                 result += ' ' + lyxrc.print_landscape_flag;
1707         return result;
1708 }
1709
1710
1711 string const BufferParams::babelCall(string const & lang_opts) const
1712 {
1713         string lang_pack = lyxrc.language_package;
1714         if (lang_pack != "\\usepackage{babel}")
1715                 return lang_pack;
1716         // suppress the babel call when there is no babel language defined
1717         // for the document language in the lib/languages file and if no
1718         // other languages are used (lang_opts is then empty)
1719         if (lang_opts.empty())
1720                 return string();
1721         // when Vietnamese is used, babel must directly be loaded with the
1722         // language options, see
1723         // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129417.html
1724         size_t viet = lang_opts.find("vietnam");
1725         // viet = string::npos when not found
1726         if (!lyxrc.language_global_options || viet != string::npos)
1727                 return "\\usepackage[" + lang_opts + "]{babel}";
1728         return lang_pack;
1729 }
1730
1731
1732 void BufferParams::writeEncodingPreamble(odocstream & os,
1733                 LaTeXFeatures & features, TexRow & texrow) const
1734 {
1735         if (inputenc == "auto") {
1736                 string const doc_encoding =
1737                         language->encoding()->latexName();
1738                 Encoding::Package const package =
1739                         language->encoding()->package();
1740
1741                 // Create a list with all the input encodings used
1742                 // in the document
1743                 set<string> encodings =
1744                         features.getEncodingSet(doc_encoding);
1745
1746                 // When the encodings EUC-JP-plain, JIS-plain, or SJIS-plainare used, the
1747                 // package inputenc must be omitted. Therefore set the encoding to empty.
1748                 // see http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html
1749                 if (doc_encoding == "EUC-JP-plain" || doc_encoding == "JIS-plain" ||
1750                         doc_encoding == "SJIS-plain")
1751                         encodings.clear();
1752
1753                 if (!encodings.empty() || package == Encoding::inputenc) {
1754                         os << "\\usepackage[";
1755                         set<string>::const_iterator it = encodings.begin();
1756                         set<string>::const_iterator const end = encodings.end();
1757                         if (it != end) {
1758                                 os << from_ascii(*it);
1759                                 ++it;
1760                         }
1761                         for (; it != end; ++it)
1762                                 os << ',' << from_ascii(*it);
1763                         if (package == Encoding::inputenc) {
1764                                 if (!encodings.empty())
1765                                         os << ',';
1766                                 os << from_ascii(doc_encoding);
1767                         }
1768                         os << "]{inputenc}\n";
1769                         texrow.newline();
1770                 }
1771                 if (package == Encoding::CJK || features.mustProvide("CJK")) {
1772                         os << "\\usepackage{CJK}\n";
1773                         texrow.newline();
1774                 }
1775         } else if (inputenc != "default") {
1776                 switch (encoding().package()) {
1777                 case Encoding::none:
1778                         break;
1779                 case Encoding::inputenc:
1780                         os << "\\usepackage[" << from_ascii(inputenc)
1781                            << "]{inputenc}\n";
1782                         texrow.newline();
1783                         break;
1784                 case Encoding::CJK:
1785                         os << "\\usepackage{CJK}\n";
1786                         texrow.newline();
1787                         break;
1788                 }
1789         }
1790
1791         // The encoding "armscii8" is only available when the package "armtex" is loaded.
1792         // armscii8 is used for Armenian.
1793         if (language->encoding()->latexName() == "armscii8" || inputenc == "armscii8") {
1794                 os << "\\usepackage{armtex}\n";
1795                 texrow.newline();
1796         }
1797 }
1798
1799
1800 string const BufferParams::loadFonts(string const & rm,
1801                                      string const & sf, string const & tt,
1802                                      bool const & sc, bool const & osf,
1803                                      int const & sfscale, int const & ttscale) const
1804 {
1805         /* The LaTeX font world is in a flux. In the PSNFSS font interface,
1806            several packages have been replaced by others, that might not
1807            be installed on every system. We have to take care for that
1808            (see psnfss.pdf). We try to support all psnfss fonts as well
1809            as the fonts that have become de facto standard in the LaTeX
1810            world (e.g. Latin Modern). We do not support obsolete fonts
1811            (like PSLatex). In general, it should be possible to mix any
1812            rm font with any sf or tt font, respectively. (JSpitzm)
1813            TODO:
1814                 -- separate math fonts.
1815         */
1816
1817         if (rm == "default" && sf == "default" && tt == "default")
1818                 //nothing to do
1819                 return string();
1820
1821         ostringstream os;
1822
1823         // ROMAN FONTS
1824         // Computer Modern (must be explicitely selectable -- there might be classes
1825         // that define a different default font!
1826         if (rm == "cmr") {
1827                 os << "\\renewcommand{\\rmdefault}{cmr}\n";
1828                 // osf for Computer Modern needs eco.sty
1829                 if (osf)
1830                         os << "\\usepackage{eco}\n";
1831         }
1832         // Latin Modern Roman
1833         else if (rm == "lmodern")
1834                 os << "\\usepackage{lmodern}\n";
1835         // AE
1836         else if (rm == "ae") {
1837                 // not needed when using OT1 font encoding.
1838                 if (lyxrc.fontenc != "default")
1839                         os << "\\usepackage{ae,aecompl}\n";
1840         }
1841         // Times
1842         else if (rm == "times") {
1843                 // try to load the best available package
1844                 if (LaTeXFeatures::isAvailable("mathptmx"))
1845                         os << "\\usepackage{mathptmx}\n";
1846                 else if (LaTeXFeatures::isAvailable("mathptm"))
1847                         os << "\\usepackage{mathptm}\n";
1848                 else
1849                         os << "\\usepackage{times}\n";
1850         }
1851         // Palatino
1852         else if (rm == "palatino") {
1853                 // try to load the best available package
1854                 if (LaTeXFeatures::isAvailable("mathpazo")) {
1855                         os << "\\usepackage";
1856                         if (osf || sc) {
1857                                 os << '[';
1858                                 if (!osf)
1859                                         os << "sc";
1860                                 else
1861                                         // "osf" includes "sc"!
1862                                         os << "osf";
1863                                 os << ']';
1864                         }
1865                         os << "{mathpazo}\n";
1866                 }
1867                 else if (LaTeXFeatures::isAvailable("mathpple"))
1868                         os << "\\usepackage{mathpple}\n";
1869                 else
1870                         os << "\\usepackage{palatino}\n";
1871         }
1872         // Utopia
1873         else if (rm == "utopia") {
1874                 // fourier supersedes utopia.sty, but does
1875                 // not work with OT1 encoding.
1876                 if (LaTeXFeatures::isAvailable("fourier")
1877                     && lyxrc.fontenc != "default") {
1878                         os << "\\usepackage";
1879                         if (osf || sc) {
1880                                 os << '[';
1881                                 if (sc)
1882                                         os << "expert";
1883                                 if (osf && sc)
1884                                         os << ',';
1885                                 if (osf)
1886                                         os << "oldstyle";
1887                                 os << ']';
1888                         }
1889                         os << "{fourier}\n";
1890                 }
1891                 else
1892                         os << "\\usepackage{utopia}\n";
1893         }
1894         // Bera (complete fontset)
1895         else if (rm == "bera" && sf == "default" && tt == "default")
1896                 os << "\\usepackage{bera}\n";
1897         // everything else
1898         else if (rm != "default")
1899                 os << "\\usepackage" << "{" << rm << "}\n";
1900
1901         // SANS SERIF
1902         // Helvetica, Bera Sans
1903         if (sf == "helvet" || sf == "berasans") {
1904                 if (sfscale != 100)
1905                         os << "\\usepackage[scaled=" << float(sfscale) / 100
1906                            << "]{" << sf << "}\n";
1907                 else
1908                         os << "\\usepackage{" << sf << "}\n";
1909         }
1910         // Avant Garde
1911         else if (sf == "avant")
1912                 os << "\\usepackage{" << sf << "}\n";
1913         // Computer Modern, Latin Modern, CM Bright
1914         else if (sf != "default")
1915                 os << "\\renewcommand{\\sfdefault}{" << sf << "}\n";
1916
1917         // monospaced/typewriter
1918         // Courier, LuxiMono
1919         if (tt == "luximono" || tt == "beramono") {
1920                 if (ttscale != 100)
1921                         os << "\\usepackage[scaled=" << float(ttscale) / 100
1922                            << "]{" << tt << "}\n";
1923                 else
1924                         os << "\\usepackage{" << tt << "}\n";
1925         }
1926         // Courier
1927         else if (tt == "courier" )
1928                 os << "\\usepackage{" << tt << "}\n";
1929         // Computer Modern, Latin Modern, CM Bright
1930         else if  (tt != "default")
1931                 os << "\\renewcommand{\\ttdefault}{" << tt << "}\n";
1932
1933         return os.str();
1934 }
1935
1936
1937 Encoding const & BufferParams::encoding() const
1938 {
1939         if (inputenc == "auto" || inputenc == "default")
1940                 return *(language->encoding());
1941         Encoding const * const enc =
1942                 encodings.getFromLaTeXName(inputenc);
1943         if (enc)
1944                 return *enc;
1945         lyxerr << "Unknown inputenc value `" << inputenc
1946                << "'. Using `auto' instead." << endl;
1947         return *(language->encoding());
1948 }
1949
1950
1951 biblio::CiteEngine BufferParams::getEngine() const
1952 {
1953         // FIXME the class should provide the numerical/
1954         // authoryear choice
1955         if (documentClass().provides("natbib")
1956             && cite_engine_ != biblio::ENGINE_NATBIB_NUMERICAL)
1957                 return biblio::ENGINE_NATBIB_AUTHORYEAR;
1958         return cite_engine_;
1959 }
1960
1961
1962 void BufferParams::setCiteEngine(biblio::CiteEngine const cite_engine)
1963 {
1964         cite_engine_ = cite_engine;
1965 }
1966
1967 } // namespace lyx