]> git.lyx.org Git - lyx.git/blob - src/bufferparams.C
LFUN_UNICODE_INSERT - unicode-insert
[lyx.git] / src / bufferparams.C
1 /**
2  * \file bufferparams.C
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 "BranchList.h"
22 #include "Bullet.h"
23 #include "debug.h"
24 #include "encoding.h"
25 #include "gettext.h"
26 #include "language.h"
27 #include "LaTeXFeatures.h"
28 #include "LColor.h"
29 #include "lyxfont.h"
30 #include "lyxlex.h"
31 #include "lyxrc.h"
32 #include "lyxtextclasslist.h"
33 #include "outputparams.h"
34 #include "tex-strings.h"
35 #include "Spacing.h"
36 #include "texrow.h"
37 #include "vspace.h"
38
39 #include "frontends/Alert.h"
40
41 #include "support/lyxalgo.h" // for lyx::count
42 #include "support/convert.h"
43 #include "support/translator.h"
44
45 #include <boost/array.hpp>
46
47 #include <sstream>
48
49
50 namespace lyx {
51
52 using support::bformat;
53 using support::rtrim;
54 using support::tokenPos;
55
56 using std::endl;
57 using std::string;
58 using std::istringstream;
59 using std::ostream;
60 using std::ostringstream;
61 using std::pair;
62
63 namespace Alert = lyx::frontend::Alert;
64
65
66 // Local translators
67 namespace {
68
69 // Paragraph separation
70 typedef Translator<string, BufferParams::PARSEP> ParSepTranslator;
71
72
73 ParSepTranslator const init_parseptranslator()
74 {
75         ParSepTranslator translator(string_paragraph_separation[0], BufferParams::PARSEP_INDENT);
76         translator.addPair(string_paragraph_separation[1], BufferParams::PARSEP_SKIP);
77         return translator;
78 }
79
80
81 ParSepTranslator const & parseptranslator()
82 {
83         static ParSepTranslator translator = init_parseptranslator();
84         return translator;
85 }
86
87
88 // Quotes language
89 typedef Translator<string, InsetQuotes::quote_language> QuotesLangTranslator;
90
91
92 QuotesLangTranslator const init_quoteslangtranslator()
93 {
94         QuotesLangTranslator translator(string_quotes_language[0], InsetQuotes::EnglishQ);
95         translator.addPair(string_quotes_language[1], InsetQuotes::SwedishQ);
96         translator.addPair(string_quotes_language[2], InsetQuotes::GermanQ);
97         translator.addPair(string_quotes_language[3], InsetQuotes::PolishQ);
98         translator.addPair(string_quotes_language[4], InsetQuotes::FrenchQ);
99         translator.addPair(string_quotes_language[5], InsetQuotes::DanishQ);
100         return translator;
101 }
102
103
104 QuotesLangTranslator const & quoteslangtranslator()
105 {
106         static QuotesLangTranslator translator = init_quoteslangtranslator();
107         return translator;
108 }
109
110
111 // Paper size
112 typedef Translator<std::string, PAPER_SIZE> PaperSizeTranslator;
113
114
115 PaperSizeTranslator const init_papersizetranslator()
116 {
117         PaperSizeTranslator translator(string_papersize[0], PAPER_DEFAULT);
118         translator.addPair(string_papersize[1], PAPER_CUSTOM);
119         translator.addPair(string_papersize[2], PAPER_USLETTER);
120         translator.addPair(string_papersize[3], PAPER_USLEGAL);
121         translator.addPair(string_papersize[4], PAPER_USEXECUTIVE);
122         translator.addPair(string_papersize[5], PAPER_A3);
123         translator.addPair(string_papersize[6], PAPER_A4);
124         translator.addPair(string_papersize[7], PAPER_A5);
125         translator.addPair(string_papersize[8], PAPER_B3);
126         translator.addPair(string_papersize[9], PAPER_B4);
127         translator.addPair(string_papersize[10], PAPER_B5);
128         return translator;
129 }
130
131
132 PaperSizeTranslator const & papersizetranslator()
133 {
134         static PaperSizeTranslator translator = init_papersizetranslator();
135         return translator;
136 }
137
138
139 // Paper orientation
140 typedef Translator<string, PAPER_ORIENTATION> PaperOrientationTranslator;
141
142
143 PaperOrientationTranslator const init_paperorientationtranslator()
144 {
145         PaperOrientationTranslator translator(string_orientation[0], ORIENTATION_PORTRAIT);
146         translator.addPair(string_orientation[1], ORIENTATION_LANDSCAPE);
147         return translator;
148 }
149
150
151 PaperOrientationTranslator const & paperorientationtranslator()
152 {
153         static PaperOrientationTranslator translator = init_paperorientationtranslator();
154         return translator;
155 }
156
157
158 // Page sides
159 typedef Translator<int, LyXTextClass::PageSides> SidesTranslator;
160
161
162 SidesTranslator const init_sidestranslator()
163 {
164         SidesTranslator translator(1, LyXTextClass::OneSide);
165         translator.addPair(2, LyXTextClass::TwoSides);
166         return translator;
167 }
168
169
170 SidesTranslator const & sidestranslator()
171 {
172         static SidesTranslator translator = init_sidestranslator();
173         return translator;
174 }
175
176
177
178 // AMS
179 typedef Translator<int, BufferParams::AMS> AMSTranslator;
180
181
182 AMSTranslator const init_amstranslator()
183 {
184         AMSTranslator translator(0, BufferParams::AMS_OFF);
185         translator.addPair(1, BufferParams::AMS_AUTO);
186         translator.addPair(2, BufferParams::AMS_ON);
187         return translator;
188 }
189
190
191 AMSTranslator const & amstranslator()
192 {
193         static AMSTranslator translator = init_amstranslator();
194         return translator;
195 }
196
197
198 // Cite engine
199 typedef Translator<string, biblio::CiteEngine> CiteEngineTranslator;
200
201
202 CiteEngineTranslator const init_citeenginetranslator()
203 {
204         CiteEngineTranslator translator("basic", biblio::ENGINE_BASIC);
205         translator.addPair("natbib_numerical", biblio::ENGINE_NATBIB_NUMERICAL);
206         translator.addPair("natbib_authoryear", biblio::ENGINE_NATBIB_AUTHORYEAR);
207         translator.addPair("jurabib", biblio::ENGINE_JURABIB);
208         return translator;
209 }
210
211
212 CiteEngineTranslator const & citeenginetranslator()
213 {
214         static CiteEngineTranslator translator = init_citeenginetranslator();
215         return translator;
216 }
217
218
219 // Spacing
220 typedef Translator<string, Spacing::Space> SpaceTranslator;
221
222
223 SpaceTranslator const init_spacetranslator()
224 {
225         SpaceTranslator translator("default", Spacing::Default);
226         translator.addPair("single", Spacing::Single);
227         translator.addPair("onehalf", Spacing::Onehalf);
228         translator.addPair("double", Spacing::Double);
229         translator.addPair("other", Spacing::Other);
230         return translator;
231 }
232
233
234 SpaceTranslator const & spacetranslator()
235 {
236         static SpaceTranslator translator = init_spacetranslator();
237         return translator;
238 }
239
240 // ends annonym namespace
241 }
242
243
244 class BufferParams::Impl
245 {
246 public:
247         Impl();
248
249         AuthorList authorlist;
250         BranchList branchlist;
251         boost::array<Bullet, 4> temp_bullets;
252         boost::array<Bullet, 4> user_defined_bullets;
253         Spacing spacing;
254         /** This is the amount of space used for paragraph_separation "skip",
255          * and for detached paragraphs in "indented" documents.
256          */
257         VSpace defskip;
258 };
259
260
261 BufferParams::Impl::Impl()
262         : defskip(VSpace::MEDSKIP)
263 {
264         // set initial author
265         authorlist.record(Author(lyxrc.user_name, lyxrc.user_email));
266 }
267
268
269 BufferParams::Impl *
270 BufferParams::MemoryTraits::clone(BufferParams::Impl const * ptr)
271 {
272         BOOST_ASSERT(ptr);
273
274         return new BufferParams::Impl(*ptr);
275 }
276
277
278 void BufferParams::MemoryTraits::destroy(BufferParams::Impl * ptr)
279 {
280         delete ptr;
281 }
282
283
284 BufferParams::BufferParams()
285         : // Initialize textclass to point to article. if `first' is
286           // true in the returned pair, then `second' is the textclass
287           // number; if it is false, second is 0. In both cases, second
288           // is what we want.
289         textclass(textclasslist.numberOfClass("article").second),
290         pimpl_(new Impl)
291 {
292         paragraph_separation = PARSEP_INDENT;
293         quotes_language = InsetQuotes::EnglishQ;
294         fontsize = "default";
295
296         /*  PaperLayout */
297         papersize = PAPER_DEFAULT;
298         orientation = ORIENTATION_PORTRAIT;
299         use_geometry = false;
300         use_amsmath = AMS_AUTO;
301         cite_engine = biblio::ENGINE_BASIC;
302         use_bibtopic = false;
303         trackChanges = false;
304         outputChanges = false;
305         secnumdepth = 3;
306         tocdepth = 3;
307         language = default_language;
308         fontsRoman = "default";
309         fontsSans = "default";
310         fontsTypewriter = "default";
311         fontsDefaultFamily = "default";
312         fontsSC = false;
313         fontsOSF = false;
314         fontsSansScale = 100;
315         fontsTypewriterScale = 100;
316         inputenc = "auto";
317         graphicsDriver = "default";
318         sides = LyXTextClass::OneSide;
319         columns = 1;
320         pagestyle = "default";
321         compressed = false;
322         for (int iter = 0; iter < 4; ++iter) {
323                 user_defined_bullet(iter) = ITEMIZE_DEFAULTS[iter];
324                 temp_bullet(iter) = ITEMIZE_DEFAULTS[iter];
325         }
326 }
327
328
329 BufferParams::~BufferParams()
330 {}
331
332
333 AuthorList & BufferParams::authors()
334 {
335         return pimpl_->authorlist;
336 }
337
338
339 AuthorList const & BufferParams::authors() const
340 {
341         return pimpl_->authorlist;
342 }
343
344
345 BranchList & BufferParams::branchlist()
346 {
347         return pimpl_->branchlist;
348 }
349
350
351 BranchList const & BufferParams::branchlist() const
352 {
353         return pimpl_->branchlist;
354 }
355
356
357 Bullet & BufferParams::temp_bullet(lyx::size_type const index)
358 {
359         BOOST_ASSERT(index < 4);
360         return pimpl_->temp_bullets[index];
361 }
362
363
364 Bullet const & BufferParams::temp_bullet(lyx::size_type const index) const
365 {
366         BOOST_ASSERT(index < 4);
367         return pimpl_->temp_bullets[index];
368 }
369
370
371 Bullet & BufferParams::user_defined_bullet(lyx::size_type const index)
372 {
373         BOOST_ASSERT(index < 4);
374         return pimpl_->user_defined_bullets[index];
375 }
376
377
378 Bullet const & BufferParams::user_defined_bullet(lyx::size_type const index) const
379 {
380         BOOST_ASSERT(index < 4);
381         return pimpl_->user_defined_bullets[index];
382 }
383
384
385 Spacing & BufferParams::spacing()
386 {
387         return pimpl_->spacing;
388 }
389
390
391 Spacing const & BufferParams::spacing() const
392 {
393         return pimpl_->spacing;
394 }
395
396
397 VSpace const & BufferParams::getDefSkip() const
398 {
399         return pimpl_->defskip;
400 }
401
402
403 void BufferParams::setDefSkip(VSpace const & vs)
404 {
405         pimpl_->defskip = vs;
406 }
407
408
409 string const BufferParams::readToken(LyXLex & lex, string const & token)
410 {
411         if (token == "\\textclass") {
412                 lex.next();
413                 string const classname = lex.getString();
414                 pair<bool, lyx::textclass_type> pp =
415                         textclasslist.numberOfClass(classname);
416                 if (pp.first) {
417                         textclass = pp.second;
418                 } else {
419                         // if text class does not exist, try to load it from filepath
420                         pp = textclasslist.addTextClass(classname, filepath);
421                         if (pp.first) {
422                                 textclass = pp.second;
423                         } else {
424                                 textclass = 0;
425                                 return classname;
426                         }
427                 }
428                 // FIXME: isTeXClassAvailable will try to load the layout file, but will
429                 // fail because of the lack of path info. Warnings will be given although
430                 // the layout file will be correctly loaded later.
431                 if (!getLyXTextClass().isTeXClassAvailable()) {
432                         docstring const msg =
433                                 bformat(_("The document uses a missing "
434                                                        "TeX class \"%1$s\".\n"), from_utf8(classname));
435                         Alert::warning(_("Document class not available"),
436                                        msg + _("LyX will not be able to produce output."));
437                 }
438         } else if (token == "\\begin_preamble") {
439                 readPreamble(lex);
440         } else if (token == "\\options") {
441                 lex.eatLine();
442                 options = lex.getString();
443         } else if (token == "\\language") {
444                 readLanguage(lex);
445         } else if (token == "\\inputencoding") {
446                 lex >> inputenc;
447         } else if (token == "\\graphics") {
448                 readGraphicsDriver(lex);
449         } else if (token == "\\font_roman") {
450                 lex >> fontsRoman;
451         } else if (token == "\\font_sans") {
452                 lex >> fontsSans;
453         } else if (token == "\\font_typewriter") {
454                 lex >> fontsTypewriter;
455         } else if (token == "\\font_default_family") {
456                 lex >> fontsDefaultFamily;
457         } else if (token == "\\font_sc") {
458                 lex >> fontsSC;
459         } else if (token == "\\font_osf") {
460                 lex >> fontsOSF;
461         } else if (token == "\\font_sf_scale") {
462                 lex >> fontsSansScale;
463         } else if (token == "\\font_tt_scale") {
464                 lex >> fontsTypewriterScale;
465         } else if (token == "\\paragraph_separation") {
466                 string parsep;
467                 lex >> parsep;
468                 paragraph_separation = parseptranslator().find(parsep);
469         } else if (token == "\\defskip") {
470                 lex.next();
471                 pimpl_->defskip = VSpace(lex.getString());
472         } else if (token == "\\quotes_language") {
473                 string quotes_lang;
474                 lex >> quotes_lang;
475                 quotes_language = quoteslangtranslator().find(quotes_lang);
476         } else if (token == "\\papersize") {
477                 string ppsize;
478                 lex >> ppsize;
479                 papersize = papersizetranslator().find(ppsize);
480         } else if (token == "\\use_geometry") {
481                 lex >> use_geometry;
482         } else if (token == "\\use_amsmath") {
483                 int use_ams;
484                 lex >> use_ams;
485                 use_amsmath = amstranslator().find(use_ams);
486         } else if (token == "\\cite_engine") {
487                 string engine;
488                 lex >> engine;
489                 cite_engine = citeenginetranslator().find(engine);
490         } else if (token == "\\use_bibtopic") {
491                 lex >> use_bibtopic;
492         } else if (token == "\\tracking_changes") {
493                 lex >> trackChanges;
494         } else if (token == "\\output_changes") {
495                 lex >> outputChanges;
496         } else if (token == "\\branch") {
497                 lex.next();
498                 string branch = lex.getString();
499                 branchlist().add(branch);
500                 while (true) {
501                         lex.next();
502                         string const tok = lex.getString();
503                         if (tok == "\\end_branch")
504                                 break;
505                         Branch * branch_ptr = branchlist().find(branch);
506                         if (tok == "\\selected") {
507                                 lex.next();
508                                 if (branch_ptr)
509                                         branch_ptr->setSelected(lex.getInteger());
510                         }
511                         // not yet operational
512                         if (tok == "\\color") {
513                                 lex.eatLine();
514                                 string color = lex.getString();
515                                 if (branch_ptr)
516                                         branch_ptr->setColor(color);
517                                 // Update also the LColor table:
518                                 if (color == "none")
519                                         color = lcolor.getX11Name(LColor::background);
520                                 lcolor.setColor(branch, color);
521
522                         }
523                 }
524         } else if (token == "\\author") {
525                 lex.eatLine();
526                 istringstream ss(lex.getString());
527                 Author a;
528                 ss >> a;
529                 author_map.push_back(pimpl_->authorlist.record(a));
530         } else if (token == "\\paperorientation") {
531                 string orient;
532                 lex >> orient;
533                 orientation = paperorientationtranslator().find(orient);
534         } else if (token == "\\paperwidth") {
535                 lex >> paperwidth;
536         } else if (token == "\\paperheight") {
537                 lex >> paperheight;
538         } else if (token == "\\leftmargin") {
539                 lex >> leftmargin;
540         } else if (token == "\\topmargin") {
541                 lex >> topmargin;
542         } else if (token == "\\rightmargin") {
543                 lex >> rightmargin;
544         } else if (token == "\\bottommargin") {
545                 lex >> bottommargin;
546         } else if (token == "\\headheight") {
547                 lex >> headheight;
548         } else if (token == "\\headsep") {
549                 lex >> headsep;
550         } else if (token == "\\footskip") {
551                 lex >> footskip;
552         } else if (token == "\\paperfontsize") {
553                 lex >> fontsize;
554         } else if (token == "\\papercolumns") {
555                 lex >> columns;
556         } else if (token == "\\papersides") {
557                 int psides;
558                 lex >> psides;
559                 sides = sidestranslator().find(psides);
560         } else if (token == "\\paperpagestyle") {
561                 lex >> pagestyle;
562         } else if (token == "\\bullet") {
563                 readBullets(lex);
564         } else if (token == "\\bulletLaTeX") {
565                 readBulletsLaTeX(lex);
566         } else if (token == "\\secnumdepth") {
567                 lex >> secnumdepth;
568         } else if (token == "\\tocdepth") {
569                 lex >> tocdepth;
570         } else if (token == "\\spacing") {
571                 string nspacing;
572                 lex >> nspacing;
573                 string tmp_val;
574                 if (nspacing == "other") {
575                         lex >> tmp_val;
576                 }
577                 spacing().set(spacetranslator().find(nspacing), tmp_val);
578         } else if (token == "\\float_placement") {
579                 lex >> float_placement;
580         } else {
581                 return token;
582         }
583
584         return string();
585 }
586
587
588 void BufferParams::writeFile(ostream & os) const
589 {
590         // The top of the file is written by the buffer.
591         // Prints out the buffer info into the .lyx file given by file
592
593         // the textclass
594         os << "\\textclass " << textclasslist[textclass].name() << '\n';
595
596         // then the the preamble
597         if (!preamble.empty()) {
598                 // remove '\n' from the end of preamble
599                 string const tmppreamble = rtrim(preamble, "\n");
600                 os << "\\begin_preamble\n"
601                    << tmppreamble
602                    << "\n\\end_preamble\n";
603         }
604
605         // the options
606         if (!options.empty()) {
607                 os << "\\options " << options << '\n';
608         }
609
610         // then the text parameters
611         if (language != ignore_language)
612                 os << "\\language " << language->lang() << '\n';
613         os << "\\inputencoding " << inputenc
614            << "\n\\font_roman " << fontsRoman
615            << "\n\\font_sans " << fontsSans
616            << "\n\\font_typewriter " << fontsTypewriter
617            << "\n\\font_default_family " << fontsDefaultFamily
618            << "\n\\font_sc " << convert<string>(fontsSC)
619            << "\n\\font_osf " << convert<string>(fontsOSF)
620            << "\n\\font_sf_scale " << fontsSansScale
621            << "\n\\font_tt_scale " << fontsTypewriterScale
622            << "\n\\graphics " << graphicsDriver << '\n';
623
624         if (!float_placement.empty()) {
625                 os << "\\float_placement " << float_placement << '\n';
626         }
627         os << "\\paperfontsize " << fontsize << '\n';
628
629         spacing().writeFile(os);
630
631         os << "\\papersize " << string_papersize[papersize]
632            << "\n\\use_geometry " << convert<string>(use_geometry)
633            << "\n\\use_amsmath " << use_amsmath
634            << "\n\\cite_engine " << citeenginetranslator().find(cite_engine)
635            << "\n\\use_bibtopic " << convert<string>(use_bibtopic)
636            << "\n\\paperorientation " << string_orientation[orientation]
637            << '\n';
638
639         BranchList::const_iterator it = branchlist().begin();
640         BranchList::const_iterator end = branchlist().end();
641         for (; it != end; ++it) {
642                 os << "\\branch " << it->getBranch()
643                    << "\n\\selected " << it->getSelected()
644                    << "\n\\color " << lyx::X11hexname(it->getColor())
645                    << "\n\\end_branch"
646                    << "\n";
647         }
648
649         if (!paperwidth.empty())
650                 os << "\\paperwidth "
651                    << VSpace(paperwidth).asLyXCommand() << '\n';
652         if (!paperheight.empty())
653                 os << "\\paperheight "
654                    << VSpace(paperheight).asLyXCommand() << '\n';
655         if (!leftmargin.empty())
656                 os << "\\leftmargin "
657                    << VSpace(leftmargin).asLyXCommand() << '\n';
658         if (!topmargin.empty())
659                 os << "\\topmargin "
660                    << VSpace(topmargin).asLyXCommand() << '\n';
661         if (!rightmargin.empty())
662                 os << "\\rightmargin "
663                    << VSpace(rightmargin).asLyXCommand() << '\n';
664         if (!bottommargin.empty())
665                 os << "\\bottommargin "
666                    << VSpace(bottommargin).asLyXCommand() << '\n';
667         if (!headheight.empty())
668                 os << "\\headheight "
669                    << VSpace(headheight).asLyXCommand() << '\n';
670         if (!headsep.empty())
671                 os << "\\headsep "
672                    << VSpace(headsep).asLyXCommand() << '\n';
673         if (!footskip.empty())
674                 os << "\\footskip "
675                    << VSpace(footskip).asLyXCommand() << '\n';
676         os << "\\secnumdepth " << secnumdepth
677            << "\n\\tocdepth " << tocdepth
678            << "\n\\paragraph_separation "
679            << string_paragraph_separation[paragraph_separation]
680            << "\n\\defskip " << getDefSkip().asLyXCommand()
681            << "\n\\quotes_language "
682            << string_quotes_language[quotes_language]
683            << "\n\\papercolumns " << columns
684            << "\n\\papersides " << sides
685            << "\n\\paperpagestyle " << pagestyle << '\n';
686         for (int i = 0; i < 4; ++i) {
687                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
688                         if (user_defined_bullet(i).getFont() != -1) {
689                                 os << "\\bullet " << i << " "
690                                    << user_defined_bullet(i).getFont() << " "
691                                    << user_defined_bullet(i).getCharacter() << " "
692                                    << user_defined_bullet(i).getSize() << "\n";
693                         }
694                         else {
695                                 // FIXME UNICODE
696                                 os << "\\bulletLaTeX " << i << " \""
697                                    << lyx::to_ascii(user_defined_bullet(i).getText())
698                                    << "\"\n";
699                         }
700                 }
701         }
702
703         os << "\\tracking_changes " << convert<string>(trackChanges) << "\n";
704         os << "\\output_changes " << convert<string>(outputChanges) << "\n";
705
706         AuthorList::Authors::const_iterator a_it = pimpl_->authorlist.begin();
707         AuthorList::Authors::const_iterator a_end = pimpl_->authorlist.end();
708         for (; a_it != a_end; ++a_it) {
709                 os << "\\author " << a_it->second << "\n";
710         }
711 }
712
713
714 bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features,
715                               TexRow & texrow) const
716 {
717         os << "\\documentclass";
718
719         LyXTextClass const & tclass = getLyXTextClass();
720
721         ostringstream clsoptions; // the document class options.
722
723         if (tokenPos(tclass.opt_fontsize(),
724                      '|', fontsize) >= 0) {
725                 // only write if existing in list (and not default)
726                 clsoptions << fontsize << "pt,";
727         }
728
729         // custom, A3, B3 and B4 paper sizes need geometry
730         bool nonstandard_papersize = papersize == PAPER_B3 
731                 || papersize == PAPER_B4
732                 || papersize == PAPER_A3
733                 || papersize == PAPER_CUSTOM;
734
735         if (!use_geometry) {
736                 switch (papersize) {
737                 case PAPER_A4:
738                         clsoptions << "a4paper,";
739                         break;
740                 case PAPER_USLETTER:
741                         clsoptions << "letterpaper,";
742                         break;
743                 case PAPER_A5:
744                         clsoptions << "a5paper,";
745                         break;
746                 case PAPER_B5:
747                         clsoptions << "b5paper,";
748                         break;
749                 case PAPER_USEXECUTIVE:
750                         clsoptions << "executivepaper,";
751                         break;
752                 case PAPER_USLEGAL:
753                         clsoptions << "legalpaper,";
754                         break;
755                 case PAPER_DEFAULT:
756                 case PAPER_A3:
757                 case PAPER_B3:
758                 case PAPER_B4:
759                 case PAPER_CUSTOM:
760                         break;
761                 }
762         }
763
764         // if needed
765         if (sides != tclass.sides()) {
766                 switch (sides) {
767                 case LyXTextClass::OneSide:
768                         clsoptions << "oneside,";
769                         break;
770                 case LyXTextClass::TwoSides:
771                         clsoptions << "twoside,";
772                         break;
773                 }
774         }
775
776         // if needed
777         if (columns != tclass.columns()) {
778                 if (columns == 2)
779                         clsoptions << "twocolumn,";
780                 else
781                         clsoptions << "onecolumn,";
782         }
783
784         if (!use_geometry
785             && orientation == ORIENTATION_LANDSCAPE)
786                 clsoptions << "landscape,";
787
788         // language should be a parameter to \documentclass
789         if (language->babel() == "hebrew"
790             && default_language->babel() != "hebrew")
791                 // This seems necessary
792                 features.useLanguage(default_language);
793
794         ostringstream language_options;
795         bool const use_babel = features.useBabel();
796         if (use_babel) {
797                 language_options << features.getLanguages();
798                 language_options << language->babel();
799                 if (lyxrc.language_global_options)
800                         clsoptions << language_options.str() << ',';
801         }
802
803         // the user-defined options
804         if (!options.empty()) {
805                 clsoptions << options << ',';
806         }
807
808         string strOptions(clsoptions.str());
809         if (!strOptions.empty()) {
810                 strOptions = rtrim(strOptions, ",");
811                 // FIXME UNICODE
812                 os << '[' << from_utf8(strOptions) << ']';
813         }
814
815         os << '{' << from_ascii(tclass.latexname()) << "}\n";
816         texrow.newline();
817         // end of \documentclass defs
818
819         // font selection must be done before loading fontenc.sty
820         string const fonts =
821                 loadFonts(features, fontsRoman, fontsSans,
822                           fontsTypewriter, fontsSC, fontsOSF,
823                           fontsSansScale, fontsTypewriterScale);
824         if (!fonts.empty()) {
825                 os << from_ascii(fonts);
826                 texrow.newline();
827         }
828         if (fontsDefaultFamily != "default")
829                 os << "\\renewcommand{\\familydefault}{\\"
830                    << from_ascii(fontsDefaultFamily) << "}\n";
831         // this one is not per buffer
832         if (lyxrc.fontenc != "default") {
833                 os << "\\usepackage[" << from_ascii(lyxrc.fontenc)
834                    << "]{fontenc}\n";
835                 texrow.newline();
836         }
837
838         // TODO: Some people want to support more encodings than UTF-8. They can have a field day around here
839         if (true) {
840                 os << "\\usepackage[utf8]{inputenc}\n";
841                 texrow.newline();
842         } else {
843                 if (inputenc == "auto") {
844                         string const doc_encoding =
845                                 language->encoding()->latexName();
846
847                         // Create a list with all the input encodings used
848                         // in the document
849                         std::set<string> encodings =
850                                 features.getEncodingSet(doc_encoding);
851
852                         os << "\\usepackage[";
853                         std::set<string>::const_iterator it = encodings.begin();
854                         std::set<string>::const_iterator const end = encodings.end();
855                         for (; it != end; ++it)
856                                 os << from_ascii(*it) << ',';
857                         os << from_ascii(doc_encoding) << "]{inputenc}\n";
858                         texrow.newline();
859                 } else if (inputenc != "default") {
860                         os << "\\usepackage[" << from_ascii(inputenc)
861                            << "]{inputenc}\n";
862                         texrow.newline();
863                 }
864         }
865
866         if (use_geometry || nonstandard_papersize) {
867                 os << "\\usepackage{geometry}\n";
868                 texrow.newline();
869                 os << "\\geometry{verbose";
870                 if (orientation == ORIENTATION_LANDSCAPE)
871                         os << ",landscape";
872                 switch (papersize) {
873                 case PAPER_CUSTOM:
874                         if (!paperwidth.empty())
875                                 os << ",paperwidth="
876                                    << from_ascii(paperwidth);
877                         if (!paperheight.empty())
878                                 os << ",paperheight="
879                                    << from_ascii(paperheight);
880                         break;
881                 case PAPER_USLETTER:
882                         os << ",letterpaper";
883                         break;
884                 case PAPER_USLEGAL:
885                         os << ",legalpaper";
886                         break;
887                 case PAPER_USEXECUTIVE:
888                         os << ",executivepaper";
889                         break;
890                 case PAPER_A3:
891                         os << ",a3paper";
892                         break;
893                 case PAPER_A4:
894                         os << ",a4paper";
895                         break;
896                 case PAPER_A5:
897                         os << ",a5paper";
898                         break;
899                 case PAPER_B3:
900                         os << ",b3paper";
901                         break;
902                 case PAPER_B4:
903                         os << ",b4paper";
904                         break;
905                 case PAPER_B5:
906                         os << ",b5paper";
907                         break;
908                 default:
909                         // default papersize ie PAPER_DEFAULT
910                         switch (lyxrc.default_papersize) {
911                         case PAPER_DEFAULT: // keep compiler happy
912                         case PAPER_USLETTER:
913                                 os << ",letterpaper";
914                                 break;
915                         case PAPER_USLEGAL:
916                                 os << ",legalpaper";
917                                 break;
918                         case PAPER_USEXECUTIVE:
919                                 os << ",executivepaper";
920                                 break;
921                         case PAPER_A3:
922                                 os << ",a3paper";
923                                 break;
924                         case PAPER_A4:
925                                 os << ",a4paper";
926                                 break;
927                         case PAPER_A5:
928                                 os << ",a5paper";
929                                 break;
930                         case PAPER_B5:
931                                 os << ",b5paper";
932                                 break;
933                         case PAPER_B3:
934                         case PAPER_B4:
935                         case PAPER_CUSTOM:
936                                 break;
937                         }
938                 }
939                 if (!topmargin.empty())
940                         os << ",tmargin=" << from_ascii(topmargin);
941                 if (!bottommargin.empty())
942                         os << ",bmargin=" << from_ascii(bottommargin);
943                 if (!leftmargin.empty())
944                         os << ",lmargin=" << from_ascii(leftmargin);
945                 if (!rightmargin.empty())
946                         os << ",rmargin=" << from_ascii(rightmargin);
947                 if (!headheight.empty())
948                         os << ",headheight=" << from_ascii(headheight);
949                 if (!headsep.empty())
950                         os << ",headsep=" << from_ascii(headsep);
951                 if (!footskip.empty())
952                         os << ",footskip=" << from_ascii(footskip);
953                 os << "}\n";
954                 texrow.newline();
955         }
956
957         if (tokenPos(tclass.opt_pagestyle(),
958                      '|', pagestyle) >= 0) {
959                 if (pagestyle == "fancy") {
960                         os << "\\usepackage{fancyhdr}\n";
961                         texrow.newline();
962                 }
963                 os << "\\pagestyle{" << from_ascii(pagestyle) << "}\n";
964                 texrow.newline();
965         }
966
967         // Only if class has a ToC hierarchy
968         if (tclass.hasTocLevels()) {
969                 if (secnumdepth != tclass.secnumdepth()) {
970                         os << "\\setcounter{secnumdepth}{"
971                            << secnumdepth
972                            << "}\n";
973                         texrow.newline();
974                 }
975                 if (tocdepth != tclass.tocdepth()) {
976                         os << "\\setcounter{tocdepth}{"
977                            << tocdepth
978                            << "}\n";
979                         texrow.newline();
980                 }
981         }
982
983         if (paragraph_separation) {
984                 switch (getDefSkip().kind()) {
985                 case VSpace::SMALLSKIP:
986                         os << "\\setlength\\parskip{\\smallskipamount}\n";
987                         break;
988                 case VSpace::MEDSKIP:
989                         os << "\\setlength\\parskip{\\medskipamount}\n";
990                         break;
991                 case VSpace::BIGSKIP:
992                         os << "\\setlength\\parskip{\\bigskipamount}\n";
993                         break;
994                 case VSpace::LENGTH:
995                         os << "\\setlength\\parskip{"
996                            << from_utf8(getDefSkip().length().asLatexString())
997                            << "}\n";
998                         break;
999                 default: // should never happen // Then delete it.
1000                         os << "\\setlength\\parskip{\\medskipamount}\n";
1001                         break;
1002                 }
1003                 texrow.newline();
1004
1005                 os << "\\setlength\\parindent{0pt}\n";
1006                 texrow.newline();
1007         }
1008
1009         // If we use jurabib, we have to call babel here.
1010         if (use_babel && features.isRequired("jurabib")) {
1011                 os << from_ascii(babelCall(language_options.str()))
1012                    << '\n'
1013                    << from_ascii(features.getBabelOptions());
1014                 texrow.newline();
1015         }
1016
1017         // Now insert the LyX specific LaTeX commands...
1018
1019         // The optional packages;
1020         string lyxpreamble(features.getPackages());
1021
1022         // this might be useful...
1023         lyxpreamble += "\n\\makeatletter\n";
1024
1025         // Some macros LyX will need
1026         string tmppreamble(features.getMacros());
1027
1028         if (!tmppreamble.empty()) {
1029                 lyxpreamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1030                         "LyX specific LaTeX commands.\n"
1031                         + tmppreamble + '\n';
1032         }
1033
1034         // the text class specific preamble
1035         tmppreamble = features.getTClassPreamble();
1036         if (!tmppreamble.empty()) {
1037                 lyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1038                         "Textclass specific LaTeX commands.\n"
1039                         + tmppreamble + '\n';
1040         }
1041
1042         /* the user-defined preamble */
1043         if (!preamble.empty()) {
1044                 lyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1045                         "User specified LaTeX commands.\n"
1046                         + preamble + '\n';
1047         }
1048
1049         // Itemize bullet settings need to be last in case the user
1050         // defines their own bullets that use a package included
1051         // in the user-defined preamble -- ARRae
1052         // Actually it has to be done much later than that
1053         // since some packages like frenchb make modifications
1054         // at \begin{document} time -- JMarc
1055         string bullets_def;
1056         for (int i = 0; i < 4; ++i) {
1057                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1058                         if (bullets_def.empty())
1059                                 bullets_def="\\AtBeginDocument{\n";
1060                         bullets_def += "  \\def\\labelitemi";
1061                         switch (i) {
1062                                 // `i' is one less than the item to modify
1063                         case 0:
1064                                 break;
1065                         case 1:
1066                                 bullets_def += 'i';
1067                                 break;
1068                         case 2:
1069                                 bullets_def += "ii";
1070                                 break;
1071                         case 3:
1072                                 bullets_def += 'v';
1073                                 break;
1074                         }
1075                         // FIXME UNICODE
1076                         bullets_def += '{' +
1077                                 lyx::to_ascii(user_defined_bullet(i).getText())
1078                                 + "}\n";
1079                 }
1080         }
1081
1082         if (!bullets_def.empty())
1083                 lyxpreamble += bullets_def + "}\n\n";
1084
1085         // We try to load babel late, in case it interferes
1086         // with other packages.
1087         // Jurabib has to be called after babel, though.
1088         if (use_babel && !features.isRequired("jurabib")) {
1089                 lyxpreamble += babelCall(language_options.str()) + '\n';
1090                 lyxpreamble += features.getBabelOptions();
1091         }
1092
1093         lyxpreamble += "\\makeatother\n";
1094
1095         // dvipost settings come after everything else
1096         if (features.isAvailable("dvipost") && outputChanges) {
1097                 lyxpreamble +=
1098                         "\\dvipostlayout\n"
1099                         "\\dvipost{osstart color push Red}\n"
1100                         "\\dvipost{osend color pop}\n"
1101                         "\\dvipost{cbstart color push Blue}\n"
1102                         "\\dvipost{cbend color pop}\n";
1103         }
1104
1105         int const nlines =
1106                 int(lyx::count(lyxpreamble.begin(), lyxpreamble.end(), '\n'));
1107         for (int j = 0; j != nlines; ++j) {
1108                 texrow.newline();
1109         }
1110
1111         // FIXME UNICODE
1112         os << from_utf8(lyxpreamble);
1113         return use_babel;
1114 }
1115
1116
1117 void BufferParams::useClassDefaults()
1118 {
1119         LyXTextClass const & tclass = textclasslist[textclass];
1120
1121         sides = tclass.sides();
1122         columns = tclass.columns();
1123         pagestyle = tclass.pagestyle();
1124         options = tclass.options();
1125         // Only if class has a ToC hierarchy
1126         if (tclass.hasTocLevels()) {
1127                 secnumdepth = tclass.secnumdepth();
1128                 tocdepth = tclass.tocdepth();
1129         }
1130 }
1131
1132
1133 bool BufferParams::hasClassDefaults() const
1134 {
1135         LyXTextClass const & tclass = textclasslist[textclass];
1136
1137         return (sides == tclass.sides()
1138                 && columns == tclass.columns()
1139                 && pagestyle == tclass.pagestyle()
1140                 && options == tclass.options()
1141                 && secnumdepth == tclass.secnumdepth()
1142                 && tocdepth == tclass.tocdepth());
1143 }
1144
1145
1146 LyXTextClass const & BufferParams::getLyXTextClass() const
1147 {
1148         return textclasslist[textclass];
1149 }
1150
1151
1152 LyXFont const BufferParams::getFont() const
1153 {
1154         LyXFont f = getLyXTextClass().defaultfont();
1155         f.setLanguage(language);
1156         if (fontsDefaultFamily == "rmdefault")
1157                 f.setFamily(LyXFont::ROMAN_FAMILY);
1158         else if (fontsDefaultFamily == "sfdefault")
1159                 f.setFamily(LyXFont::SANS_FAMILY);
1160         else if (fontsDefaultFamily == "ttdefault")
1161                 f.setFamily(LyXFont::TYPEWRITER_FAMILY);
1162         return f;
1163 }
1164
1165
1166 void BufferParams::readPreamble(LyXLex & lex)
1167 {
1168         if (lex.getString() != "\\begin_preamble")
1169                 lyxerr << "Error (BufferParams::readPreamble):"
1170                         "consistency check failed." << endl;
1171
1172         preamble = lex.getLongString("\\end_preamble");
1173 }
1174
1175
1176 void BufferParams::readLanguage(LyXLex & lex)
1177 {
1178         if (!lex.next()) return;
1179
1180         string const tmptok = lex.getString();
1181
1182         // check if tmptok is part of tex_babel in tex-defs.h
1183         language = languages.getLanguage(tmptok);
1184         if (!language) {
1185                 // Language tmptok was not found
1186                 language = default_language;
1187                 lyxerr << "Warning: Setting language `"
1188                        << tmptok << "' to `" << language->lang()
1189                        << "'." << endl;
1190         }
1191 }
1192
1193
1194 void BufferParams::readGraphicsDriver(LyXLex & lex)
1195 {
1196         if (!lex.next()) return;
1197
1198         string const tmptok = lex.getString();
1199         // check if tmptok is part of tex_graphics in tex_defs.h
1200         int n = 0;
1201         while (true) {
1202                 string const test = tex_graphics[n++];
1203
1204                 if (test == tmptok) {
1205                         graphicsDriver = tmptok;
1206                         break;
1207                 } else if (test == "") {
1208                         lex.printError(
1209                                 "Warning: graphics driver `$$Token' not recognized!\n"
1210                                 "         Setting graphics driver to `default'.\n");
1211                         graphicsDriver = "default";
1212                         break;
1213                 }
1214         }
1215 }
1216
1217
1218 void BufferParams::readBullets(LyXLex & lex)
1219 {
1220         if (!lex.next()) return;
1221
1222         int const index = lex.getInteger();
1223         lex.next();
1224         int temp_int = lex.getInteger();
1225         user_defined_bullet(index).setFont(temp_int);
1226         temp_bullet(index).setFont(temp_int);
1227         lex >> temp_int;
1228         user_defined_bullet(index).setCharacter(temp_int);
1229         temp_bullet(index).setCharacter(temp_int);
1230         lex >> temp_int;
1231         user_defined_bullet(index).setSize(temp_int);
1232         temp_bullet(index).setSize(temp_int);
1233 }
1234
1235
1236 void BufferParams::readBulletsLaTeX(LyXLex & lex)
1237 {
1238         // The bullet class should be able to read this.
1239         if (!lex.next()) return;
1240         int const index = lex.getInteger();
1241         lex.next(true);
1242         docstring const temp_str = lex.getDocString();
1243
1244         user_defined_bullet(index).setText(temp_str);
1245         temp_bullet(index).setText(temp_str);
1246 }
1247
1248
1249 string const BufferParams::paperSizeName() const
1250 {
1251         char real_papersize = papersize;
1252         if (real_papersize == PAPER_DEFAULT)
1253                 real_papersize = lyxrc.default_papersize;
1254
1255         switch (real_papersize) {
1256         case PAPER_A3:
1257                 return "a3";
1258         case PAPER_A4:
1259                 return "a4";
1260         case PAPER_A5:
1261                 return "a5";
1262         case PAPER_B5:
1263                 return "b5";
1264         case PAPER_USEXECUTIVE:
1265                 return "foolscap";
1266         case PAPER_USLEGAL:
1267                 return "legal";
1268         case PAPER_USLETTER:
1269         default:
1270                 return "letter";
1271         }
1272 }
1273
1274
1275 string const BufferParams::dvips_options() const
1276 {
1277         string result;
1278
1279         if (use_geometry
1280             && papersize == PAPER_CUSTOM
1281             && !lyxrc.print_paper_dimension_flag.empty()
1282             && !paperwidth.empty()
1283             && !paperheight.empty()) {
1284                 // using a custom papersize
1285                 result = lyxrc.print_paper_dimension_flag;
1286                 result += ' ' + paperwidth;
1287                 result += ',' + paperheight;
1288         } else {
1289                 string const paper_option = paperSizeName();
1290                 if (paper_option != "letter" ||
1291                     orientation != ORIENTATION_LANDSCAPE) {
1292                         // dvips won't accept -t letter -t landscape.
1293                         // In all other cases, include the paper size
1294                         // explicitly.
1295                         result = lyxrc.print_paper_flag;
1296                         result += ' ' + paper_option;
1297                 }
1298         }
1299         if (orientation == ORIENTATION_LANDSCAPE &&
1300             papersize != PAPER_CUSTOM)
1301                 result += ' ' + lyxrc.print_landscape_flag;
1302         return result;
1303 }
1304
1305
1306 string const BufferParams::babelCall(string const & lang_opts) const
1307 {
1308         string tmp = lyxrc.language_package;
1309         if (!lyxrc.language_global_options && tmp == "\\usepackage{babel}")
1310                 tmp = string("\\usepackage[") + lang_opts + "]{babel}";
1311         return tmp;
1312 }
1313
1314
1315 string const BufferParams::loadFonts(LaTeXFeatures & features, string const & rm,
1316                                      string const & sf, string const & tt,
1317                                      bool const & sc, bool const & osf,
1318                                      int const & sfscale, int const & ttscale) const
1319 {
1320         /* The LaTeX font world is in a flux. In the PSNFSS font interface,
1321            several packages have been replaced by others, that might not
1322            be installed on every system. We have to take care for that
1323            (see psnfss.pdf). We try to support all psnfss fonts as well
1324            as the fonts that have become de facto standard in the LaTeX
1325            world (e.g. Latin Modern). We do not support obsolete fonts
1326            (like PSLatex). In general, it should be possible to mix any
1327            rm font with any sf or tt font, respectively. (JSpitzm)
1328            TODO:
1329                 -- separate math fonts.
1330         */
1331
1332         if (rm == "default" && sf == "default" && tt == "default")
1333                 //nothing to do
1334                 return string();
1335
1336         ostringstream os;
1337
1338         // ROMAN FONTS
1339         // Computer Modern (must be explicitely selectable -- there might be classes
1340         // that define a different default font!
1341         if (rm == "cmr") {
1342                 os << "\\renewcommand{\\rmdefault}{cmr}\n";
1343                 // osf for Computer Modern needs eco.sty
1344                 if (osf)
1345                         os << "\\usepackage{eco}\n";
1346         }
1347         // Latin Modern Roman
1348         else if (rm == "lmodern")
1349                 os << "\\usepackage{lmodern}\n";
1350         // AE
1351         else if (rm == "ae") {
1352                 // not needed when using OT1 font encoding.
1353                 if (lyxrc.fontenc != "default")
1354                         os << "\\usepackage{ae,aecompl}\n";
1355         }
1356         // Times
1357         else if (rm == "times") {
1358                 // try to load the best available package
1359                 if (features.isAvailable("mathptmx"))
1360                         os << "\\usepackage{mathptmx}\n";
1361                 else if (features.isAvailable("mathptm"))
1362                         os << "\\usepackage{mathptm}\n";
1363                 else
1364                         os << "\\usepackage{times}\n";
1365         }
1366         // Palatino
1367         else if (rm == "palatino") {
1368                 // try to load the best available package
1369                 if (features.isAvailable("mathpazo")) {
1370                         os << "\\usepackage";
1371                         if (osf || sc) {
1372                                 os << '[';
1373                                 if (!osf)
1374                                         os << "sc";
1375                                 else
1376                                         // "osf" includes "sc"!
1377                                         os << "osf";
1378                                 os << ']';
1379                         }
1380                         os << "{mathpazo}\n";
1381                 }
1382                 else if (features.isAvailable("mathpple"))
1383                         os << "\\usepackage{mathpple}\n";
1384                 else
1385                         os << "\\usepackage{palatino}\n";
1386         }
1387         // Utopia
1388         else if (rm == "utopia") {
1389                 // fourier supersedes utopia.sty, but does
1390                 // not work with OT1 encoding.
1391                 if (features.isAvailable("fourier")
1392                     && lyxrc.fontenc != "default") {
1393                         os << "\\usepackage";
1394                         if (osf || sc) {
1395                                 os << '[';
1396                                 if (sc)
1397                                         os << "expert";
1398                                 if (osf && sc)
1399                                         os << ',';
1400                                 if (osf)
1401                                         os << "oldstyle";
1402                                 os << ']';
1403                         }
1404                         os << "{fourier}\n";
1405                 }
1406                 else
1407                         os << "\\usepackage{utopia}\n";
1408         }
1409         // Bera (complete fontset)
1410         else if (rm == "bera" && sf == "default" && tt == "default")
1411                 os << "\\usepackage{bera}\n";
1412         // everything else
1413         else if (rm != "default")
1414                 os << "\\usepackage" << "{" << rm << "}\n";
1415
1416         // SANS SERIF
1417         // Helvetica, Bera Sans
1418         if (sf == "helvet" || sf == "berasans") {
1419                 if (sfscale != 100)
1420                         os << "\\usepackage[scaled=" << float(sfscale) / 100
1421                            << "]{" << sf << "}\n";
1422                 else
1423                         os << "\\usepackage{" << sf << "}\n";
1424         }
1425         // Avant Garde
1426         else if (sf == "avant")
1427                 os << "\\usepackage{" << sf << "}\n";
1428         // Computer Modern, Latin Modern, CM Bright
1429         else if (sf != "default")
1430                 os << "\\renewcommand{\\sfdefault}{" << sf << "}\n";
1431
1432         // monospaced/typewriter
1433         // Courier, LuxiMono
1434         if (tt == "luximono" || tt == "beramono") {
1435                 if (ttscale != 100)
1436                         os << "\\usepackage[scaled=" << float(ttscale) / 100
1437                            << "]{" << tt << "}\n";
1438                 else
1439                         os << "\\usepackage{" << tt << "}\n";
1440         }
1441         // Courier
1442         else if (tt == "courier" )
1443                 os << "\\usepackage{" << tt << "}\n";
1444         // Computer Modern, Latin Modern, CM Bright
1445         else if  (tt != "default")
1446                 os << "\\renewcommand{\\ttdefault}{" << tt << "}\n";
1447
1448         return os.str();
1449 }
1450
1451
1452 } // namespace lyx