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