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