]> git.lyx.org Git - lyx.git/blob - src/BufferParams.cpp
UserGuide.lyx: clarify the description of image settings grouping
[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 "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 == "\\options") {
491                 lex.eatLine();
492                 options = lex.getString();
493         } else if (token == "\\master") {
494                 lex.eatLine();
495                 master = lex.getString();
496         } else if (token == "\\language") {
497                 readLanguage(lex);
498         } else if (token == "\\inputencoding") {
499                 lex >> inputenc;
500         } else if (token == "\\graphics") {
501                 readGraphicsDriver(lex);
502         } else if (token == "\\font_roman") {
503                 lex >> fontsRoman;
504         } else if (token == "\\font_sans") {
505                 lex >> fontsSans;
506         } else if (token == "\\font_typewriter") {
507                 lex >> fontsTypewriter;
508         } else if (token == "\\font_default_family") {
509                 lex >> fontsDefaultFamily;
510         } else if (token == "\\font_sc") {
511                 lex >> fontsSC;
512         } else if (token == "\\font_osf") {
513                 lex >> fontsOSF;
514         } else if (token == "\\font_sf_scale") {
515                 lex >> fontsSansScale;
516         } else if (token == "\\font_tt_scale") {
517                 lex >> fontsTypewriterScale;
518         } else if (token == "\\font_cjk") {
519                 lex >> fontsCJK;
520         } else if (token == "\\paragraph_separation") {
521                 string parsep;
522                 lex >> parsep;
523                 paragraph_separation = parseptranslator().find(parsep);
524         } else if (token == "\\defskip") {
525                 lex.next();
526                 string defskip = lex.getString();
527                 if (defskip == "defskip")
528                         // this is invalid
529                         defskip = "medskip";
530                 pimpl_->defskip = VSpace(defskip);
531         } else if (token == "\\quotes_language") {
532                 string quotes_lang;
533                 lex >> quotes_lang;
534                 quotes_language = quoteslangtranslator().find(quotes_lang);
535         } else if (token == "\\papersize") {
536                 string ppsize;
537                 lex >> ppsize;
538                 papersize = papersizetranslator().find(ppsize);
539         } else if (token == "\\use_geometry") {
540                 lex >> use_geometry;
541         } else if (token == "\\use_amsmath") {
542                 int use_ams;
543                 lex >> use_ams;
544                 use_amsmath = packagetranslator().find(use_ams);
545         } else if (token == "\\use_esint") {
546                 int useesint;
547                 lex >> useesint;
548                 use_esint = packagetranslator().find(useesint);
549         } else if (token == "\\cite_engine") {
550                 string engine;
551                 lex >> engine;
552                 cite_engine_ = citeenginetranslator().find(engine);
553         } else if (token == "\\use_bibtopic") {
554                 lex >> use_bibtopic;
555         } else if (token == "\\tracking_changes") {
556                 lex >> trackChanges;
557         } else if (token == "\\output_changes") {
558                 lex >> outputChanges;
559         } else if (token == "\\branch") {
560                 lex.next();
561                 docstring branch = lex.getDocString();
562                 branchlist().add(branch);
563                 while (true) {
564                         lex.next();
565                         string const tok = lex.getString();
566                         if (tok == "\\end_branch")
567                                 break;
568                         Branch * branch_ptr = branchlist().find(branch);
569                         if (tok == "\\selected") {
570                                 lex.next();
571                                 if (branch_ptr)
572                                         branch_ptr->setSelected(lex.getInteger());
573                         }
574                         // not yet operational
575                         if (tok == "\\color") {
576                                 lex.eatLine();
577                                 string color = lex.getString();
578                                 if (branch_ptr)
579                                         branch_ptr->setColor(color);
580                                 // Update also the Color table:
581                                 if (color == "none")
582                                         color = lcolor.getX11Name(Color_background);
583                                 // FIXME UNICODE
584                                 lcolor.setColor(to_utf8(branch), color);
585
586                         }
587                 }
588         } else if (token == "\\author") {
589                 lex.eatLine();
590                 istringstream ss(lex.getString());
591                 Author a;
592                 ss >> a;
593                 author_map.push_back(pimpl_->authorlist.record(a));
594         } else if (token == "\\paperorientation") {
595                 string orient;
596                 lex >> orient;
597                 orientation = paperorientationtranslator().find(orient);
598         } else if (token == "\\paperwidth") {
599                 lex >> paperwidth;
600         } else if (token == "\\paperheight") {
601                 lex >> paperheight;
602         } else if (token == "\\leftmargin") {
603                 lex >> leftmargin;
604         } else if (token == "\\topmargin") {
605                 lex >> topmargin;
606         } else if (token == "\\rightmargin") {
607                 lex >> rightmargin;
608         } else if (token == "\\bottommargin") {
609                 lex >> bottommargin;
610         } else if (token == "\\headheight") {
611                 lex >> headheight;
612         } else if (token == "\\headsep") {
613                 lex >> headsep;
614         } else if (token == "\\footskip") {
615                 lex >> footskip;
616         } else if (token == "\\columnsep") {
617                 lex >> columnsep;
618         } else if (token == "\\paperfontsize") {
619                 lex >> fontsize;
620         } else if (token == "\\papercolumns") {
621                 lex >> columns;
622         } else if (token == "\\listings_params") {
623                 string par;
624                 lex >> par;
625                 listings_params = InsetListingsParams(par).params();
626         } else if (token == "\\papersides") {
627                 int psides;
628                 lex >> psides;
629                 sides = sidestranslator().find(psides);
630         } else if (token == "\\paperpagestyle") {
631                 lex >> pagestyle;
632         } else if (token == "\\bullet") {
633                 readBullets(lex);
634         } else if (token == "\\bulletLaTeX") {
635                 readBulletsLaTeX(lex);
636         } else if (token == "\\secnumdepth") {
637                 lex >> secnumdepth;
638         } else if (token == "\\tocdepth") {
639                 lex >> tocdepth;
640         } else if (token == "\\spacing") {
641                 string nspacing;
642                 lex >> nspacing;
643                 string tmp_val;
644                 if (nspacing == "other") {
645                         lex >> tmp_val;
646                 }
647                 spacing().set(spacetranslator().find(nspacing), tmp_val);
648         } else if (token == "\\float_placement") {
649                 lex >> float_placement;
650
651         } else if (prefixIs(token, "\\pdf_") || token == "\\use_hyperref") {
652                 string toktmp = pdfoptions().readToken(lex, token);
653                 if (!toktmp.empty()) {
654                         lyxerr << "PDFOptions::readToken(): Unknown token: " <<
655                                 toktmp << endl;
656                         return toktmp;
657                 }
658         } else {
659                 lyxerr << "BufferParams::readToken(): Unknown token: " << 
660                         token << endl;
661                 return token;
662         }
663
664         return string();
665 }
666
667
668 void BufferParams::writeFile(ostream & os) const
669 {
670         // The top of the file is written by the buffer.
671         // Prints out the buffer info into the .lyx file given by file
672
673         // the textclass
674         os << "\\textclass " << baseClass()->name() << '\n';
675
676         // then the preamble
677         if (!preamble.empty()) {
678                 // remove '\n' from the end of preamble
679                 string const tmppreamble = rtrim(preamble, "\n");
680                 os << "\\begin_preamble\n"
681                    << tmppreamble
682                    << "\n\\end_preamble\n";
683         }
684
685         // the options
686         if (!options.empty()) {
687                 os << "\\options " << options << '\n';
688         }
689
690         // the master document
691         if (!master.empty()) {
692                 os << "\\master " << master << '\n';
693         }
694         
695         //the modules
696         if (!layoutModules_.empty()) {
697                 os << "\\begin_modules" << '\n';
698                 LayoutModuleList::const_iterator it = layoutModules_.begin();
699                 for (; it != layoutModules_.end(); it++)
700                         os << *it << '\n';
701                 os << "\\end_modules" << '\n';
702         }
703
704         // local layout information
705         if (!local_layout.empty()) {
706                 // remove '\n' from the end 
707                 string const tmplocal = rtrim(local_layout, "\n");
708                 os << "\\begin_local_layout\n"
709                    << tmplocal
710                    << "\n\\end_local_layout\n";
711         }
712
713         // then the text parameters
714         if (language != ignore_language)
715                 os << "\\language " << language->lang() << '\n';
716         os << "\\inputencoding " << inputenc
717            << "\n\\font_roman " << fontsRoman
718            << "\n\\font_sans " << fontsSans
719            << "\n\\font_typewriter " << fontsTypewriter
720            << "\n\\font_default_family " << fontsDefaultFamily
721            << "\n\\font_sc " << convert<string>(fontsSC)
722            << "\n\\font_osf " << convert<string>(fontsOSF)
723            << "\n\\font_sf_scale " << fontsSansScale
724            << "\n\\font_tt_scale " << fontsTypewriterScale
725            << '\n';
726         if (!fontsCJK.empty()) {
727                 os << "\\font_cjk " << fontsCJK << '\n';
728         }
729         os << "\n\\graphics " << graphicsDriver << '\n';
730
731         if (!float_placement.empty()) {
732                 os << "\\float_placement " << float_placement << '\n';
733         }
734         os << "\\paperfontsize " << fontsize << '\n';
735
736         spacing().writeFile(os);
737         pdfoptions().writeFile(os);
738
739         os << "\\papersize " << string_papersize[papersize]
740            << "\n\\use_geometry " << convert<string>(use_geometry)
741            << "\n\\use_amsmath " << use_amsmath
742            << "\n\\use_esint " << use_esint
743            << "\n\\cite_engine " << citeenginetranslator().find(cite_engine_)
744            << "\n\\use_bibtopic " << convert<string>(use_bibtopic)
745            << "\n\\paperorientation " << string_orientation[orientation]
746            << '\n';
747
748         BranchList::const_iterator it = branchlist().begin();
749         BranchList::const_iterator end = branchlist().end();
750         for (; it != end; ++it) {
751                 os << "\\branch " << to_utf8(it->getBranch())
752                    << "\n\\selected " << it->getSelected()
753                    << "\n\\color " << lyx::X11hexname(it->getColor())
754                    << "\n\\end_branch"
755                    << "\n";
756         }
757
758         if (!paperwidth.empty())
759                 os << "\\paperwidth "
760                    << VSpace(paperwidth).asLyXCommand() << '\n';
761         if (!paperheight.empty())
762                 os << "\\paperheight "
763                    << VSpace(paperheight).asLyXCommand() << '\n';
764         if (!leftmargin.empty())
765                 os << "\\leftmargin "
766                    << VSpace(leftmargin).asLyXCommand() << '\n';
767         if (!topmargin.empty())
768                 os << "\\topmargin "
769                    << VSpace(topmargin).asLyXCommand() << '\n';
770         if (!rightmargin.empty())
771                 os << "\\rightmargin "
772                    << VSpace(rightmargin).asLyXCommand() << '\n';
773         if (!bottommargin.empty())
774                 os << "\\bottommargin "
775                    << VSpace(bottommargin).asLyXCommand() << '\n';
776         if (!headheight.empty())
777                 os << "\\headheight "
778                    << VSpace(headheight).asLyXCommand() << '\n';
779         if (!headsep.empty())
780                 os << "\\headsep "
781                    << VSpace(headsep).asLyXCommand() << '\n';
782         if (!footskip.empty())
783                 os << "\\footskip "
784                    << VSpace(footskip).asLyXCommand() << '\n';
785         if (!columnsep.empty())
786                 os << "\\columnsep " 
787                          << VSpace(columnsep).asLyXCommand() << '\n';
788         os << "\\secnumdepth " << secnumdepth
789            << "\n\\tocdepth " << tocdepth
790            << "\n\\paragraph_separation "
791            << string_paragraph_separation[paragraph_separation]
792            << "\n\\defskip " << getDefSkip().asLyXCommand()
793            << "\n\\quotes_language "
794            << string_quotes_language[quotes_language]
795            << "\n\\papercolumns " << columns
796            << "\n\\papersides " << sides
797            << "\n\\paperpagestyle " << pagestyle << '\n';
798         if (!listings_params.empty())
799                 os << "\\listings_params \"" <<
800                         InsetListingsParams(listings_params).encodedString() << "\"\n";
801         for (int i = 0; i < 4; ++i) {
802                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
803                         if (user_defined_bullet(i).getFont() != -1) {
804                                 os << "\\bullet " << i << " "
805                                    << user_defined_bullet(i).getFont() << " "
806                                    << user_defined_bullet(i).getCharacter() << " "
807                                    << user_defined_bullet(i).getSize() << "\n";
808                         }
809                         else {
810                                 // FIXME UNICODE
811                                 os << "\\bulletLaTeX " << i << " \""
812                                    << lyx::to_ascii(user_defined_bullet(i).getText())
813                                    << "\"\n";
814                         }
815                 }
816         }
817
818         os << "\\tracking_changes " << convert<string>(trackChanges) << "\n";
819         os << "\\output_changes " << convert<string>(outputChanges) << "\n";
820
821         AuthorList::Authors::const_iterator a_it = pimpl_->authorlist.begin();
822         AuthorList::Authors::const_iterator a_end = pimpl_->authorlist.end();
823         for (; a_it != a_end; ++a_it) {
824                 if (a_it->second.used())
825                         os << "\\author " << a_it->second << "\n";
826                 else
827                         os << "\\author " << Author() << "\n";
828         }
829 }
830
831
832 void BufferParams::validate(LaTeXFeatures & features) const
833 {
834         features.require(documentClass().requires());
835
836         if (outputChanges) {
837                 bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
838                 bool xcolorsoul = LaTeXFeatures::isAvailable("soul") &&
839                                   LaTeXFeatures::isAvailable("xcolor");
840
841                 switch (features.runparams().flavor) {
842                 case OutputParams::LATEX:
843                         if (dvipost) {
844                                 features.require("ct-dvipost");
845                                 features.require("dvipost");
846                         } else if (xcolorsoul) {
847                                 features.require("ct-xcolor-soul");
848                                 features.require("soul");
849                                 features.require("xcolor");
850                         } else {
851                                 features.require("ct-none");
852                         }
853                         break;
854                 case OutputParams::PDFLATEX:
855                         if (xcolorsoul) {
856                                 features.require("ct-xcolor-soul");
857                                 features.require("soul");
858                                 features.require("xcolor");
859                                 // improves color handling in PDF output
860                                 features.require("pdfcolmk"); 
861                         } else {
862                                 features.require("ct-none");
863                         }
864                         break;
865                 default:
866                         break;
867                 }
868         }
869
870         // Floats with 'Here definitely' as default setting.
871         if (float_placement.find('H') != string::npos)
872                 features.require("float");
873
874         // AMS Style is at document level
875         if (use_amsmath == package_on
876             || documentClass().provides("amsmath"))
877                 features.require("amsmath");
878         if (use_esint == package_on)
879                 features.require("esint");
880
881         // Document-level line spacing
882         if (spacing().getSpace() != Spacing::Single && !spacing().isDefault())
883                 features.require("setspace");
884
885         // the bullet shapes are buffer level not paragraph level
886         // so they are tested here
887         for (int i = 0; i < 4; ++i) {
888                 if (user_defined_bullet(i) == ITEMIZE_DEFAULTS[i]) 
889                         continue;
890                 int const font = user_defined_bullet(i).getFont();
891                 if (font == 0) {
892                         int const c = user_defined_bullet(i).getCharacter();
893                         if (c == 16
894                             || c == 17
895                             || c == 25
896                             || c == 26
897                             || c == 31) {
898                                 features.require("latexsym");
899                         }
900                 } else if (font == 1) {
901                         features.require("amssymb");
902                 } else if (font >= 2 && font <= 5) {
903                         features.require("pifont");
904                 }
905         }
906
907         if (pdfoptions().use_hyperref)
908                 features.require("hyperref");
909
910         if (language->lang() == "vietnamese")
911                 features.require("vietnamese");
912         else if (language->lang() == "japanese")
913                 features.require("japanese");
914 }
915
916
917 bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features,
918                               TexRow & texrow) const
919 {
920         os << "\\documentclass";
921
922         DocumentClass const & tclass = documentClass();
923
924         ostringstream clsoptions; // the document class options.
925
926         if (tokenPos(tclass.opt_fontsize(),
927                      '|', fontsize) >= 0) {
928                 // only write if existing in list (and not default)
929                 clsoptions << fontsize << "pt,";
930         }
931
932         // custom, A3, B3 and B4 paper sizes need geometry
933         bool nonstandard_papersize = papersize == PAPER_B3
934                 || papersize == PAPER_B4
935                 || papersize == PAPER_A3
936                 || papersize == PAPER_CUSTOM;
937
938         if (!use_geometry) {
939                 switch (papersize) {
940                 case PAPER_A4:
941                         clsoptions << "a4paper,";
942                         break;
943                 case PAPER_USLETTER:
944                         clsoptions << "letterpaper,";
945                         break;
946                 case PAPER_A5:
947                         clsoptions << "a5paper,";
948                         break;
949                 case PAPER_B5:
950                         clsoptions << "b5paper,";
951                         break;
952                 case PAPER_USEXECUTIVE:
953                         clsoptions << "executivepaper,";
954                         break;
955                 case PAPER_USLEGAL:
956                         clsoptions << "legalpaper,";
957                         break;
958                 case PAPER_DEFAULT:
959                 case PAPER_A3:
960                 case PAPER_B3:
961                 case PAPER_B4:
962                 case PAPER_CUSTOM:
963                         break;
964                 }
965         }
966
967         // if needed
968         if (sides != tclass.sides()) {
969                 switch (sides) {
970                 case OneSide:
971                         clsoptions << "oneside,";
972                         break;
973                 case TwoSides:
974                         clsoptions << "twoside,";
975                         break;
976                 }
977         }
978
979         // if needed
980         if (columns != tclass.columns()) {
981                 if (columns == 2)
982                         clsoptions << "twocolumn,";
983                 else
984                         clsoptions << "onecolumn,";
985         }
986
987         if (!use_geometry
988             && orientation == ORIENTATION_LANDSCAPE)
989                 clsoptions << "landscape,";
990
991         // language should be a parameter to \documentclass
992         if (language->babel() == "hebrew"
993             && default_language->babel() != "hebrew")
994                 // This seems necessary
995                 features.useLanguage(default_language);
996
997         ostringstream language_options;
998         bool const use_babel = features.useBabel();
999         if (use_babel) {
1000                 language_options << features.getLanguages();
1001                 if (!language->babel().empty()) {
1002                         if (!language_options.str().empty())
1003                                 language_options << ',';
1004                         language_options << language->babel();
1005                 }
1006                 // if Vietnamese is used, babel must directly be loaded with the
1007                 // language options, not in the class options, see
1008                 // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129417.html
1009                 size_t viet = language_options.str().find("vietnam");
1010                 // viet = string::npos when not found
1011                 // if Japanese is used, babel must directly be loaded with the
1012                 // language options, not in the class options, see
1013                 // http://bugzilla.lyx.org/show_bug.cgi?id=4597#c4
1014                 size_t japan = language_options.str().find("japanese");
1015                 // japan = string::npos when not found
1016                 if (lyxrc.language_global_options && !language_options.str().empty()
1017                         && viet == string::npos && japan == string::npos)
1018                         clsoptions << language_options.str() << ',';
1019         }
1020
1021         // the user-defined options
1022         if (!options.empty()) {
1023                 clsoptions << options << ',';
1024         }
1025
1026         string strOptions(clsoptions.str());
1027         if (!strOptions.empty()) {
1028                 strOptions = rtrim(strOptions, ",");
1029                 // FIXME UNICODE
1030                 os << '[' << from_utf8(strOptions) << ']';
1031         }
1032
1033         os << '{' << from_ascii(tclass.latexname()) << "}\n";
1034         texrow.newline();
1035         // end of \documentclass defs
1036
1037         // font selection must be done before loading fontenc.sty
1038         string const fonts =
1039                 loadFonts(fontsRoman, fontsSans,
1040                           fontsTypewriter, fontsSC, fontsOSF,
1041                           fontsSansScale, fontsTypewriterScale);
1042         if (!fonts.empty()) {
1043                 os << from_ascii(fonts);
1044                 texrow.newline();
1045         }
1046         if (fontsDefaultFamily != "default")
1047                 os << "\\renewcommand{\\familydefault}{\\"
1048                    << from_ascii(fontsDefaultFamily) << "}\n";
1049
1050         // set font encoding
1051         // this one is not per buffer
1052         // for arabic_arabi and farsi we also need to load the LAE and LFE encoding
1053         if (lyxrc.fontenc != "default") {
1054                 if (language->lang() == "arabic_arabi" || language->lang() == "farsi") {
1055                         os << "\\usepackage[" << from_ascii(lyxrc.fontenc)
1056                            << ",LFE,LAE]{fontenc}\n";
1057                         texrow.newline();
1058                 } else {
1059                         os << "\\usepackage[" << from_ascii(lyxrc.fontenc)
1060                            << "]{fontenc}\n";
1061                         texrow.newline();
1062                 }
1063         }
1064
1065         // handle inputenc etc.
1066         writeEncodingPreamble(os, features, texrow);
1067
1068         if (!listings_params.empty()) {
1069                 os << "\\usepackage{listings}\n";
1070                 texrow.newline();
1071                 os << "\\lstset{";
1072                 // do not test validity because listings_params is supposed to be valid
1073                 string par = InsetListingsParams(listings_params).separatedParams(true);
1074                 os << from_ascii(par);
1075                 // count the number of newlines
1076                 for (size_t i = 0; i < par.size(); ++i)
1077                         if (par[i] == '\n')
1078                                 texrow.newline();
1079                 os << "}\n";
1080                 texrow.newline();
1081         }
1082         if (use_geometry || nonstandard_papersize) {
1083                 os << "\\usepackage{geometry}\n";
1084                 texrow.newline();
1085                 os << "\\geometry{verbose";
1086                 if (orientation == ORIENTATION_LANDSCAPE)
1087                         os << ",landscape";
1088                 switch (papersize) {
1089                 case PAPER_CUSTOM:
1090                         if (!paperwidth.empty())
1091                                 os << ",paperwidth="
1092                                    << from_ascii(paperwidth);
1093                         if (!paperheight.empty())
1094                                 os << ",paperheight="
1095                                    << from_ascii(paperheight);
1096                         break;
1097                 case PAPER_USLETTER:
1098                         os << ",letterpaper";
1099                         break;
1100                 case PAPER_USLEGAL:
1101                         os << ",legalpaper";
1102                         break;
1103                 case PAPER_USEXECUTIVE:
1104                         os << ",executivepaper";
1105                         break;
1106                 case PAPER_A3:
1107                         os << ",a3paper";
1108                         break;
1109                 case PAPER_A4:
1110                         os << ",a4paper";
1111                         break;
1112                 case PAPER_A5:
1113                         os << ",a5paper";
1114                         break;
1115                 case PAPER_B3:
1116                         os << ",b3paper";
1117                         break;
1118                 case PAPER_B4:
1119                         os << ",b4paper";
1120                         break;
1121                 case PAPER_B5:
1122                         os << ",b5paper";
1123                         break;
1124                 default:
1125                         // default papersize ie PAPER_DEFAULT
1126                         switch (lyxrc.default_papersize) {
1127                         case PAPER_DEFAULT: // keep compiler happy
1128                         case PAPER_USLETTER:
1129                                 os << ",letterpaper";
1130                                 break;
1131                         case PAPER_USLEGAL:
1132                                 os << ",legalpaper";
1133                                 break;
1134                         case PAPER_USEXECUTIVE:
1135                                 os << ",executivepaper";
1136                                 break;
1137                         case PAPER_A3:
1138                                 os << ",a3paper";
1139                                 break;
1140                         case PAPER_A4:
1141                                 os << ",a4paper";
1142                                 break;
1143                         case PAPER_A5:
1144                                 os << ",a5paper";
1145                                 break;
1146                         case PAPER_B5:
1147                                 os << ",b5paper";
1148                                 break;
1149                         case PAPER_B3:
1150                         case PAPER_B4:
1151                         case PAPER_CUSTOM:
1152                                 break;
1153                         }
1154                 }
1155                 if (!topmargin.empty())
1156                         os << ",tmargin=" << from_ascii(Length(topmargin).asLatexString());
1157                 if (!bottommargin.empty())
1158                         os << ",bmargin=" << from_ascii(Length(bottommargin).asLatexString());
1159                 if (!leftmargin.empty())
1160                         os << ",lmargin=" << from_ascii(Length(leftmargin).asLatexString());
1161                 if (!rightmargin.empty())
1162                         os << ",rmargin=" << from_ascii(Length(rightmargin).asLatexString());
1163                 if (!headheight.empty())
1164                         os << ",headheight=" << from_ascii(Length(headheight).asLatexString());
1165                 if (!headsep.empty())
1166                         os << ",headsep=" << from_ascii(Length(headsep).asLatexString());
1167                 if (!footskip.empty())
1168                         os << ",footskip=" << from_ascii(Length(footskip).asLatexString());
1169                 if (!columnsep.empty())
1170                         os << ",columnsep=" << from_ascii(Length(columnsep).asLatexString());
1171                 os << "}\n";
1172                 texrow.newline();
1173         } else if (orientation == ORIENTATION_LANDSCAPE) {
1174                 features.require("papersize");
1175         }
1176
1177         if (tokenPos(tclass.opt_pagestyle(),
1178                      '|', pagestyle) >= 0) {
1179                 if (pagestyle == "fancy") {
1180                         os << "\\usepackage{fancyhdr}\n";
1181                         texrow.newline();
1182                 }
1183                 os << "\\pagestyle{" << from_ascii(pagestyle) << "}\n";
1184                 texrow.newline();
1185         }
1186
1187         // Only if class has a ToC hierarchy
1188         if (tclass.hasTocLevels()) {
1189                 if (secnumdepth != tclass.secnumdepth()) {
1190                         os << "\\setcounter{secnumdepth}{"
1191                            << secnumdepth
1192                            << "}\n";
1193                         texrow.newline();
1194                 }
1195                 if (tocdepth != tclass.tocdepth()) {
1196                         os << "\\setcounter{tocdepth}{"
1197                            << tocdepth
1198                            << "}\n";
1199                         texrow.newline();
1200                 }
1201         }
1202
1203         if (paragraph_separation) {
1204                 switch (getDefSkip().kind()) {
1205                 case VSpace::SMALLSKIP:
1206                         os << "\\setlength{\\parskip}{\\smallskipamount}\n";
1207                         break;
1208                 case VSpace::MEDSKIP:
1209                         os << "\\setlength{\\parskip}{\\medskipamount}\n";
1210                         break;
1211                 case VSpace::BIGSKIP:
1212                         os << "\\setlength{\\parskip}{\\bigskipamount}\n";
1213                         break;
1214                 case VSpace::LENGTH:
1215                         os << "\\setlength{\\parskip}{"
1216                            << from_utf8(getDefSkip().length().asLatexString())
1217                            << "}\n";
1218                         break;
1219                 default: // should never happen // Then delete it.
1220                         os << "\\setlength{\\parskip}{\\medskipamount}\n";
1221                         break;
1222                 }
1223                 texrow.newline();
1224
1225                 os << "\\setlength{\\parindent}{0pt}\n";
1226                 texrow.newline();
1227         }
1228
1229         // If we use hyperref, jurabib, japanese, or vietnamese, we have to call babel here.
1230         if (use_babel
1231                 && (features.isRequired("jurabib")
1232                         || features.isRequired("hyperref")
1233                         || features.isRequired("vietnamese")
1234                         || features.isRequired("japanese") ) ) {
1235                 // FIXME UNICODE
1236                 os << from_utf8(babelCall(language_options.str()))
1237                    << '\n'
1238                    << from_utf8(features.getBabelOptions());
1239                 texrow.newline();
1240         }
1241
1242         // Now insert the LyX specific LaTeX commands...
1243
1244         // The optional packages;
1245         docstring lyxpreamble(from_ascii(features.getPackages()));
1246
1247         // Line spacing
1248         lyxpreamble += from_utf8(spacing().writePreamble(tclass.provides("SetSpace")));
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         // Will be surrounded by \makeatletter and \makeatother when needed
1266         docstring atlyxpreamble;
1267
1268         // Some macros LyX will need
1269         docstring tmppreamble(from_ascii(features.getMacros()));
1270
1271         if (!tmppreamble.empty())
1272                 atlyxpreamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1273                         "LyX specific LaTeX commands.\n"
1274                         + tmppreamble + '\n';
1275
1276         // the text class specific preamble
1277         tmppreamble = features.getTClassPreamble();
1278         if (!tmppreamble.empty())
1279                 atlyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1280                         "Textclass specific LaTeX commands.\n"
1281                         + tmppreamble + '\n';
1282
1283         /* the user-defined preamble */
1284         if (!preamble.empty())
1285                 // FIXME UNICODE
1286                 atlyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1287                         "User specified LaTeX commands.\n"
1288                         + from_utf8(preamble) + '\n';
1289
1290         // subfig loads internally the LaTeX package "caption". As
1291         // caption is a very popular package, users will load it in
1292         // the preamble. Therefore we must load subfig behind the
1293         // user-defined preamble and check if the caption package was
1294         // loaded or not. For the case that caption is loaded before
1295         // subfig, there is the subfig option "caption=false". This
1296         // option also works when a koma-script class is used and
1297         // koma's own caption commands are used instead of caption. We
1298         // use \PassOptionsToPackage here because the user could have
1299         // already loaded subfig in the preamble.
1300         if (features.isRequired("subfig")) {
1301                 atlyxpreamble += "\\@ifundefined{showcaptionsetup}{}{%\n"
1302                         " \\PassOptionsToPackage{caption=false}{subfig}}\n"
1303                         "\\usepackage{subfig}\n";
1304         }
1305
1306         // Itemize bullet settings need to be last in case the user
1307         // defines their own bullets that use a package included
1308         // in the user-defined preamble -- ARRae
1309         // Actually it has to be done much later than that
1310         // since some packages like frenchb make modifications
1311         // at \begin{document} time -- JMarc
1312         docstring bullets_def;
1313         for (int i = 0; i < 4; ++i) {
1314                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1315                         if (bullets_def.empty())
1316                                 bullets_def += "\\AtBeginDocument{\n";
1317                         bullets_def += "  \\def\\labelitemi";
1318                         switch (i) {
1319                                 // `i' is one less than the item to modify
1320                         case 0:
1321                                 break;
1322                         case 1:
1323                                 bullets_def += 'i';
1324                                 break;
1325                         case 2:
1326                                 bullets_def += "ii";
1327                                 break;
1328                         case 3:
1329                                 bullets_def += 'v';
1330                                 break;
1331                         }
1332                         bullets_def += '{' +
1333                                 user_defined_bullet(i).getText()
1334                                 + "}\n";
1335                 }
1336         }
1337
1338         if (!bullets_def.empty())
1339                 atlyxpreamble += bullets_def + "}\n\n";
1340
1341         if (atlyxpreamble.find(from_ascii("@")) != docstring::npos)
1342                 lyxpreamble += "\n\\makeatletter\n"
1343                         + atlyxpreamble + "\\makeatother\n\n";
1344         else
1345                 lyxpreamble += '\n' + atlyxpreamble;
1346
1347         // We try to load babel late, in case it interferes
1348         // with other packages.
1349         // Jurabib and Hyperref have to be called after babel, though.
1350         if (use_babel && !features.isRequired("jurabib")
1351             && !features.isRequired("hyperref")
1352             && !features.isRequired("vietnamese")
1353             && !features.isRequired("japanese")) {
1354                 // FIXME UNICODE
1355                 lyxpreamble += from_utf8(babelCall(language_options.str())) + '\n';
1356                 lyxpreamble += from_utf8(features.getBabelOptions()) + '\n';
1357         }
1358
1359         int const nlines =
1360                 int(count(lyxpreamble.begin(), lyxpreamble.end(), '\n'));
1361         for (int j = 0; j != nlines; ++j) {
1362                 texrow.newline();
1363         }
1364
1365         os << lyxpreamble;
1366         return use_babel;
1367 }
1368
1369
1370 void BufferParams::useClassDefaults()
1371 {
1372         DocumentClass const & tclass = documentClass();
1373
1374         sides = tclass.sides();
1375         columns = tclass.columns();
1376         pagestyle = tclass.pagestyle();
1377         options = tclass.options();
1378         // Only if class has a ToC hierarchy
1379         if (tclass.hasTocLevels()) {
1380                 secnumdepth = tclass.secnumdepth();
1381                 tocdepth = tclass.tocdepth();
1382         }
1383 }
1384
1385
1386 bool BufferParams::hasClassDefaults() const
1387 {
1388         DocumentClass const & tclass = documentClass();
1389
1390         return sides == tclass.sides()
1391                 && columns == tclass.columns()
1392                 && pagestyle == tclass.pagestyle()
1393                 && options == tclass.options()
1394                 && secnumdepth == tclass.secnumdepth()
1395                 && tocdepth == tclass.tocdepth();
1396 }
1397
1398
1399 DocumentClass const & BufferParams::documentClass() const
1400 {
1401         return *doc_class_;
1402 }
1403
1404
1405 DocumentClass const * BufferParams::documentClassPtr() const {
1406         return doc_class_;
1407 }
1408
1409
1410 void BufferParams::setDocumentClass(DocumentClass const * const tc) {
1411         // evil, but this function is evil
1412         doc_class_ = const_cast<DocumentClass *>(tc);
1413 }
1414
1415
1416 bool BufferParams::setBaseClass(string const & classname)
1417 {
1418         LYXERR(Debug::TCLASS, "setBaseClass: " << classname);
1419         LayoutFileList & bcl = LayoutFileList::get();
1420         if (!bcl.haveClass(classname)) {
1421                 docstring s = 
1422                         bformat(_("The document class %1$s could not be found. "
1423                                 "A default textclass with default layouts will be used. "
1424                                 "LyX might not be able to produce output unless a correct "
1425                                 "textclass is selected from the document settings dialog."),
1426                         from_utf8(classname));
1427                 frontend::Alert::error(_("Document class not found"), s);
1428                 bcl.addEmptyClass(classname);
1429         }
1430
1431         bool const success = bcl[classname].load();
1432         if (!success) { 
1433                 docstring s = 
1434                         bformat(_("The document class %1$s could not be loaded."),
1435                         from_utf8(classname));
1436                 frontend::Alert::error(_("Could not load class"), s);
1437                 return false;
1438         }
1439
1440         pimpl_->baseClass_ = classname;
1441
1442         // add any required modules not already in use
1443         set<string> const & mods = baseClass()->defaultModules();
1444         set<string>::const_iterator mit = mods.begin();
1445         set<string>::const_iterator men = mods.end();
1446         for (; mit != men; mit++) {
1447                 string const & modName = *mit;
1448                 LayoutModuleList::const_iterator const fit = 
1449                                 find(layoutModules_.begin(), layoutModules_.end(), modName);
1450                 if (fit == layoutModules_.end()) {
1451                         // We need to make sure there's no module chosen that excludes this one
1452                         LayoutModuleList::const_iterator lit = layoutModules_.begin();
1453                         LayoutModuleList::const_iterator len = layoutModules_.end();
1454                         bool foundit = false;
1455                         // so iterate over the selected modules...
1456                         for (; lit != len; lit++) {
1457                                 LyXModule * lm = moduleList[*lit];
1458                                 if (!lm)
1459                                         continue;
1460                                 vector<string> const & exc = lm->getExcludedModules();
1461                                 // ...and see if one of them excludes us.
1462                                 if (find(exc.begin(), exc.end(), modName) != exc.end()) {
1463                                         foundit = true;
1464                                         LYXERR(Debug::TCLASS, "Default module `" << modName << 
1465                                                         "' not added because excluded by loaded module `" << 
1466                                                         *lit << "'.");
1467                                         break;
1468                                 }
1469                         }
1470                         if (!foundit) {
1471                                 LYXERR(Debug::TCLASS, "Default module `" << modName << "' added.");
1472                                 layoutModules_.push_back(modName);
1473                         }
1474                 }
1475         }
1476         return true;
1477 }
1478
1479
1480 LayoutFile const * BufferParams::baseClass() const
1481 {
1482         if (LayoutFileList::get().haveClass(pimpl_->baseClass_))
1483                 return &(LayoutFileList::get()[pimpl_->baseClass_]);
1484         else 
1485                 return 0;
1486 }
1487
1488
1489 LayoutFileIndex const & BufferParams::baseClassID() const
1490 {
1491         return pimpl_->baseClass_;
1492 }
1493
1494
1495 void BufferParams::makeDocumentClass()
1496 {
1497         if (!baseClass())
1498                 return;
1499
1500         doc_class_ = &(DocumentClassBundle::get().newClass(*baseClass()));
1501
1502         // FIXME It might be worth loading the children's modules here,
1503         // just as we load their bibliographies and such, instead of just 
1504         // doing a check in InsetInclude.
1505         LayoutModuleList::const_iterator it = layoutModules_.begin();
1506         for (; it != layoutModules_.end(); it++) {
1507                 string const modName = *it;
1508                 LyXModule * lm = moduleList[modName];
1509                 if (!lm) {
1510                         docstring const msg =
1511                                 bformat(_("The module %1$s has been requested by\n"
1512                                         "this document but has not been found in the list of\n"
1513                                         "available modules. If you recently installed it, you\n"
1514                                         "probably need to reconfigure LyX.\n"), from_utf8(modName));
1515                         frontend::Alert::warning(_("Module not available"),
1516                                         msg + _("Some layouts may not be available."));
1517                         LYXERR0("BufferParams::makeDocumentClass(): Module " <<
1518                                         modName << " requested but not found in module list.");
1519                         continue;
1520                 }
1521                 if (!lm->isAvailable()) {
1522                         docstring const msg =
1523                                                 bformat(_("The module %1$s requires a package that is\n"
1524                                                 "not available in your LaTeX installation. LaTeX output\n"
1525                                                 "may not be possible.\n"), from_utf8(modName));
1526                         frontend::Alert::warning(_("Package not available"), msg);
1527                 }
1528                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1529                 if (!doc_class_->read(layout_file, TextClass::MODULE)) {
1530                         docstring const msg =
1531                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1532                         frontend::Alert::warning(_("Read Error"), msg);
1533                 }
1534         }
1535         if (!local_layout.empty()) {
1536                 if (!doc_class_->read(local_layout, TextClass::MODULE)) {
1537                         docstring const msg = _("Error reading internal layout information");
1538                         frontend::Alert::warning(_("Read Error"), msg);
1539                 }
1540         }
1541 }
1542
1543
1544 vector<string> const & BufferParams::getModules() const 
1545 {
1546         return layoutModules_;
1547 }
1548
1549
1550
1551 bool BufferParams::addLayoutModule(string const & modName) 
1552 {
1553         LayoutModuleList::const_iterator it = layoutModules_.begin();
1554         LayoutModuleList::const_iterator end = layoutModules_.end();
1555         for (; it != end; it++)
1556                 if (*it == modName) 
1557                         return false;
1558         layoutModules_.push_back(modName);
1559         return true;
1560 }
1561
1562
1563 void BufferParams::clearLayoutModules() 
1564 {
1565         layoutModules_.clear();
1566 }
1567
1568
1569 Font const BufferParams::getFont() const
1570 {
1571         FontInfo f = documentClass().defaultfont();
1572         if (fontsDefaultFamily == "rmdefault")
1573                 f.setFamily(ROMAN_FAMILY);
1574         else if (fontsDefaultFamily == "sfdefault")
1575                 f.setFamily(SANS_FAMILY);
1576         else if (fontsDefaultFamily == "ttdefault")
1577                 f.setFamily(TYPEWRITER_FAMILY);
1578         return Font(f, language);
1579 }
1580
1581
1582 void BufferParams::readPreamble(Lexer & lex)
1583 {
1584         if (lex.getString() != "\\begin_preamble")
1585                 lyxerr << "Error (BufferParams::readPreamble):"
1586                         "consistency check failed." << endl;
1587
1588         preamble = lex.getLongString("\\end_preamble");
1589 }
1590
1591
1592 void BufferParams::readLocalLayout(Lexer & lex)
1593 {
1594         if (lex.getString() != "\\begin_local_layout")
1595                 lyxerr << "Error (BufferParams::readLocalLayout):"
1596                         "consistency check failed." << endl;
1597
1598         local_layout = lex.getLongString("\\end_local_layout");
1599 }
1600
1601
1602 void BufferParams::readLanguage(Lexer & lex)
1603 {
1604         if (!lex.next()) return;
1605
1606         string const tmptok = lex.getString();
1607
1608         // check if tmptok is part of tex_babel in tex-defs.h
1609         language = languages.getLanguage(tmptok);
1610         if (!language) {
1611                 // Language tmptok was not found
1612                 language = default_language;
1613                 lyxerr << "Warning: Setting language `"
1614                        << tmptok << "' to `" << language->lang()
1615                        << "'." << endl;
1616         }
1617 }
1618
1619
1620 void BufferParams::readGraphicsDriver(Lexer & lex)
1621 {
1622         if (!lex.next()) 
1623                 return;
1624
1625         string const tmptok = lex.getString();
1626         // check if tmptok is part of tex_graphics in tex_defs.h
1627         int n = 0;
1628         while (true) {
1629                 string const test = tex_graphics[n++];
1630
1631                 if (test == tmptok) {
1632                         graphicsDriver = tmptok;
1633                         break;
1634                 }
1635                 if (test.empty()) {
1636                         lex.printError(
1637                                 "Warning: graphics driver `$$Token' not recognized!\n"
1638                                 "         Setting graphics driver to `default'.\n");
1639                         graphicsDriver = "default";
1640                         break;
1641                 }
1642         }
1643 }
1644
1645
1646 void BufferParams::readBullets(Lexer & lex)
1647 {
1648         if (!lex.next()) 
1649                 return;
1650
1651         int const index = lex.getInteger();
1652         lex.next();
1653         int temp_int = lex.getInteger();
1654         user_defined_bullet(index).setFont(temp_int);
1655         temp_bullet(index).setFont(temp_int);
1656         lex >> temp_int;
1657         user_defined_bullet(index).setCharacter(temp_int);
1658         temp_bullet(index).setCharacter(temp_int);
1659         lex >> temp_int;
1660         user_defined_bullet(index).setSize(temp_int);
1661         temp_bullet(index).setSize(temp_int);
1662 }
1663
1664
1665 void BufferParams::readBulletsLaTeX(Lexer & lex)
1666 {
1667         // The bullet class should be able to read this.
1668         if (!lex.next()) 
1669                 return;
1670         int const index = lex.getInteger();
1671         lex.next(true);
1672         docstring const temp_str = lex.getDocString();
1673
1674         user_defined_bullet(index).setText(temp_str);
1675         temp_bullet(index).setText(temp_str);
1676 }
1677
1678
1679 void BufferParams::readModules(Lexer & lex)
1680 {
1681         if (!lex.eatLine()) {
1682                 lyxerr << "Error (BufferParams::readModules):"
1683                                 "Unexpected end of input." << endl;
1684                 return;
1685         }
1686         while (true) {
1687                 string mod = lex.getString();
1688                 if (mod == "\\end_modules")
1689                         break;
1690                 addLayoutModule(mod);
1691                 lex.eatLine();
1692         }
1693 }
1694
1695
1696 string BufferParams::paperSizeName(PapersizePurpose purpose) const
1697 {
1698         char real_papersize = papersize;
1699         if (real_papersize == PAPER_DEFAULT)
1700                 real_papersize = lyxrc.default_papersize;
1701
1702         switch (real_papersize) {
1703         case PAPER_DEFAULT:
1704                 // could be anything, so don't guess
1705                 return string();
1706         case PAPER_CUSTOM: {
1707                 if (purpose == XDVI && !paperwidth.empty() &&
1708                     !paperheight.empty()) {
1709                         // heightxwidth<unit>
1710                         string first = paperwidth;
1711                         string second = paperheight;
1712                         if (orientation == ORIENTATION_LANDSCAPE)
1713                                 first.swap(second);
1714                         // cut off unit.
1715                         return first.erase(first.length() - 2)
1716                                 + "x" + second;
1717                 }
1718                 return string();
1719         }
1720         case PAPER_A3:
1721                 return "a3";
1722         case PAPER_A4:
1723                 return "a4";
1724         case PAPER_A5:
1725                 return "a5";
1726         case PAPER_B3:
1727                 // dvips and dvipdfm do not know this
1728                 if (purpose == DVIPS || purpose == DVIPDFM)
1729                         return string();
1730                 return "b3";
1731         case PAPER_B4:
1732                 // dvipdfm does not know this
1733                 if (purpose == DVIPDFM)
1734                         return string();
1735                 return "b4";
1736         case PAPER_B5:
1737                 // dvipdfm does not know this
1738                 if (purpose == DVIPDFM)
1739                         return string();
1740                 return "b5";
1741         case PAPER_USEXECUTIVE:
1742                 // dvipdfm does not know this
1743                 if (purpose == DVIPDFM)
1744                         return string();
1745                 return "foolscap";
1746         case PAPER_USLEGAL:
1747                 return "legal";
1748         case PAPER_USLETTER:
1749         default:
1750                 if (purpose == XDVI)
1751                         return "us";
1752                 return "letter";
1753         }
1754 }
1755
1756
1757 string const BufferParams::dvips_options() const
1758 {
1759         string result;
1760
1761         if (use_geometry
1762             && papersize == PAPER_CUSTOM
1763             && !lyxrc.print_paper_dimension_flag.empty()
1764             && !paperwidth.empty()
1765             && !paperheight.empty()) {
1766                 // using a custom papersize
1767                 result = lyxrc.print_paper_dimension_flag;
1768                 result += ' ' + paperwidth;
1769                 result += ',' + paperheight;
1770         } else {
1771                 string const paper_option = paperSizeName(DVIPS);
1772                 if (!paper_option.empty() && (paper_option != "letter" ||
1773                     orientation != ORIENTATION_LANDSCAPE)) {
1774                         // dvips won't accept -t letter -t landscape.
1775                         // In all other cases, include the paper size
1776                         // explicitly.
1777                         result = lyxrc.print_paper_flag;
1778                         result += ' ' + paper_option;
1779                 }
1780         }
1781         if (orientation == ORIENTATION_LANDSCAPE &&
1782             papersize != PAPER_CUSTOM)
1783                 result += ' ' + lyxrc.print_landscape_flag;
1784         return result;
1785 }
1786
1787
1788 string BufferParams::babelCall(string const & lang_opts) const
1789 {
1790         string lang_pack = lyxrc.language_package;
1791         if (lang_pack != "\\usepackage{babel}")
1792                 return lang_pack;
1793         // suppress the babel call when there is no babel language defined
1794         // for the document language in the lib/languages file and if no
1795         // other languages are used (lang_opts is then empty)
1796         if (lang_opts.empty())
1797                 return string();
1798         // If Vietnamese is used, babel must directly be loaded with the
1799         // language options, see
1800         // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129417.html
1801         size_t viet = lang_opts.find("vietnam");
1802         // viet = string::npos when not found
1803         // If Japanese is used, babel must directly be loaded with the
1804         // language options, see
1805         // http://bugzilla.lyx.org/show_bug.cgi?id=4597#c4
1806         size_t japan = lang_opts.find("japanese");
1807         // japan = string::npos when not found
1808         if (!lyxrc.language_global_options || viet != string::npos || japan != string::npos)
1809                 return "\\usepackage[" + lang_opts + "]{babel}";
1810         return lang_pack;
1811 }
1812
1813
1814 void BufferParams::writeEncodingPreamble(odocstream & os,
1815                 LaTeXFeatures & features, TexRow & texrow) const
1816 {
1817         if (inputenc == "auto") {
1818                 string const doc_encoding =
1819                         language->encoding()->latexName();
1820                 Encoding::Package const package =
1821                         language->encoding()->package();
1822
1823                 // Create a list with all the input encodings used
1824                 // in the document
1825                 set<string> encodings =
1826                         features.getEncodingSet(doc_encoding);
1827
1828                 // If the encodings EUC-JP-plain, JIS-plain, or SJIS-plain are used, the
1829                 // package inputenc must be omitted. Therefore set the encoding to empty.
1830                 // see http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html
1831                 if (doc_encoding == "EUC-JP-plain" || doc_encoding == "JIS-plain" ||
1832                         doc_encoding == "SJIS-plain")
1833                         encodings.clear();
1834
1835                 if (!encodings.empty() || package == Encoding::inputenc) {
1836                         os << "\\usepackage[";
1837                         set<string>::const_iterator it = encodings.begin();
1838                         set<string>::const_iterator const end = encodings.end();
1839                         if (it != end) {
1840                                 os << from_ascii(*it);
1841                                 ++it;
1842                         }
1843                         for (; it != end; ++it)
1844                                 os << ',' << from_ascii(*it);
1845                         if (package == Encoding::inputenc) {
1846                                 if (!encodings.empty())
1847                                         os << ',';
1848                                 os << from_ascii(doc_encoding);
1849                         }
1850                         os << "]{inputenc}\n";
1851                         texrow.newline();
1852                 }
1853                 if (package == Encoding::CJK || features.mustProvide("CJK")) {
1854                         os << "\\usepackage{CJK}\n";
1855                         texrow.newline();
1856                 }
1857         } else if (inputenc != "default") {
1858                 switch (encoding().package()) {
1859                 case Encoding::none:
1860                         break;
1861                 case Encoding::inputenc:
1862                         os << "\\usepackage[" << from_ascii(inputenc)
1863                            << "]{inputenc}\n";
1864                         texrow.newline();
1865                         break;
1866                 case Encoding::CJK:
1867                         os << "\\usepackage{CJK}\n";
1868                         texrow.newline();
1869                         break;
1870                 }
1871         }
1872
1873         // The encoding "armscii8" is only available when the package "armtex" is loaded.
1874         // armscii8 is used for Armenian.
1875         if (language->encoding()->latexName() == "armscii8" || inputenc == "armscii8") {
1876                 os << "\\usepackage{armtex}\n";
1877                 texrow.newline();
1878         }
1879 }
1880
1881
1882 string const BufferParams::loadFonts(string const & rm,
1883                                      string const & sf, string const & tt,
1884                                      bool const & sc, bool const & osf,
1885                                      int const & sfscale, int const & ttscale) const
1886 {
1887         /* The LaTeX font world is in a flux. In the PSNFSS font interface,
1888            several packages have been replaced by others, that might not
1889            be installed on every system. We have to take care for that
1890            (see psnfss.pdf). We try to support all psnfss fonts as well
1891            as the fonts that have become de facto standard in the LaTeX
1892            world (e.g. Latin Modern). We do not support obsolete fonts
1893            (like PSLatex). In general, it should be possible to mix any
1894            rm font with any sf or tt font, respectively. (JSpitzm)
1895            TODO:
1896                 -- separate math fonts.
1897         */
1898
1899         if (rm == "default" && sf == "default" && tt == "default")
1900                 //nothing to do
1901                 return string();
1902
1903         ostringstream os;
1904
1905         // ROMAN FONTS
1906         // Computer Modern (must be explicitely selectable -- there might be classes
1907         // that define a different default font!
1908         if (rm == "cmr") {
1909                 os << "\\renewcommand{\\rmdefault}{cmr}\n";
1910                 // osf for Computer Modern needs eco.sty
1911                 if (osf)
1912                         os << "\\usepackage{eco}\n";
1913         }
1914         // Latin Modern Roman
1915         else if (rm == "lmodern")
1916                 os << "\\usepackage{lmodern}\n";
1917         // AE
1918         else if (rm == "ae") {
1919                 // not needed when using OT1 font encoding.
1920                 if (lyxrc.fontenc != "default")
1921                         os << "\\usepackage{ae,aecompl}\n";
1922         }
1923         // Times
1924         else if (rm == "times") {
1925                 // try to load the best available package
1926                 if (LaTeXFeatures::isAvailable("mathptmx"))
1927                         os << "\\usepackage{mathptmx}\n";
1928                 else if (LaTeXFeatures::isAvailable("mathptm"))
1929                         os << "\\usepackage{mathptm}\n";
1930                 else
1931                         os << "\\usepackage{times}\n";
1932         }
1933         // Palatino
1934         else if (rm == "palatino") {
1935                 // try to load the best available package
1936                 if (LaTeXFeatures::isAvailable("mathpazo")) {
1937                         os << "\\usepackage";
1938                         if (osf || sc) {
1939                                 os << '[';
1940                                 if (!osf)
1941                                         os << "sc";
1942                                 else
1943                                         // "osf" includes "sc"!
1944                                         os << "osf";
1945                                 os << ']';
1946                         }
1947                         os << "{mathpazo}\n";
1948                 }
1949                 else if (LaTeXFeatures::isAvailable("mathpple"))
1950                         os << "\\usepackage{mathpple}\n";
1951                 else
1952                         os << "\\usepackage{palatino}\n";
1953         }
1954         // Utopia
1955         else if (rm == "utopia") {
1956                 // fourier supersedes utopia.sty, but does
1957                 // not work with OT1 encoding.
1958                 if (LaTeXFeatures::isAvailable("fourier")
1959                     && lyxrc.fontenc != "default") {
1960                         os << "\\usepackage";
1961                         if (osf || sc) {
1962                                 os << '[';
1963                                 if (sc)
1964                                         os << "expert";
1965                                 if (osf && sc)
1966                                         os << ',';
1967                                 if (osf)
1968                                         os << "oldstyle";
1969                                 os << ']';
1970                         }
1971                         os << "{fourier}\n";
1972                 }
1973                 else
1974                         os << "\\usepackage{utopia}\n";
1975         }
1976         // Bera (complete fontset)
1977         else if (rm == "bera" && sf == "default" && tt == "default")
1978                 os << "\\usepackage{bera}\n";
1979         // everything else
1980         else if (rm != "default")
1981                 os << "\\usepackage" << "{" << rm << "}\n";
1982
1983         // SANS SERIF
1984         // Helvetica, Bera Sans
1985         if (sf == "helvet" || sf == "berasans") {
1986                 if (sfscale != 100)
1987                         os << "\\usepackage[scaled=" << float(sfscale) / 100
1988                            << "]{" << sf << "}\n";
1989                 else
1990                         os << "\\usepackage{" << sf << "}\n";
1991         }
1992         // Avant Garde
1993         else if (sf == "avant")
1994                 os << "\\usepackage{" << sf << "}\n";
1995         // Computer Modern, Latin Modern, CM Bright
1996         else if (sf != "default")
1997                 os << "\\renewcommand{\\sfdefault}{" << sf << "}\n";
1998
1999         // monospaced/typewriter
2000         // Courier, LuxiMono
2001         if (tt == "luximono" || tt == "beramono") {
2002                 if (ttscale != 100)
2003                         os << "\\usepackage[scaled=" << float(ttscale) / 100
2004                            << "]{" << tt << "}\n";
2005                 else
2006                         os << "\\usepackage{" << tt << "}\n";
2007         }
2008         // Courier
2009         else if (tt == "courier" )
2010                 os << "\\usepackage{" << tt << "}\n";
2011         // Computer Modern, Latin Modern, CM Bright
2012         else if (tt != "default")
2013                 os << "\\renewcommand{\\ttdefault}{" << tt << "}\n";
2014
2015         return os.str();
2016 }
2017
2018
2019 Encoding const & BufferParams::encoding() const
2020 {
2021         if (inputenc == "auto" || inputenc == "default")
2022                 return *language->encoding();
2023         Encoding const * const enc = encodings.fromLaTeXName(inputenc);
2024         if (enc)
2025                 return *enc;
2026         LYXERR0("Unknown inputenc value `" << inputenc
2027                << "'. Using `auto' instead.");
2028         return *language->encoding();
2029 }
2030
2031
2032 CiteEngine BufferParams::citeEngine() const
2033 {
2034         // FIXME the class should provide the numerical/
2035         // authoryear choice
2036         if (documentClass().provides("natbib")
2037             && cite_engine_ != ENGINE_NATBIB_NUMERICAL)
2038                 return ENGINE_NATBIB_AUTHORYEAR;
2039         return cite_engine_;
2040 }
2041
2042
2043 void BufferParams::setCiteEngine(CiteEngine cite_engine)
2044 {
2045         cite_engine_ = cite_engine;
2046 }
2047
2048 } // namespace lyx