]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
More requires --> required, for C++2a.
[lyx.git] / src / TextClass.cpp
1 /**
2  * \file TextClass.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Angus Leeming
9  * \author John Levon
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "TextClass.h"
18
19 #include "LayoutFile.h"
20 #include "CiteEnginesList.h"
21 #include "Color.h"
22 #include "Counters.h"
23 #include "Floating.h"
24 #include "FloatList.h"
25 #include "Layout.h"
26 #include "Lexer.h"
27 #include "Font.h"
28 #include "ModuleList.h"
29
30 #include "frontends/alert.h"
31
32 #include "support/lassert.h"
33 #include "support/debug.h"
34 #include "support/ExceptionMessage.h"
35 #include "support/FileName.h"
36 #include "support/filetools.h"
37 #include "support/gettext.h"
38 #include "support/lstrings.h"
39 #include "support/os.h"
40 #include "support/TempFile.h"
41
42 #include <algorithm>
43 #include <fstream>
44 #include <sstream>
45
46 #ifdef ERROR
47 #undef ERROR
48 #endif
49
50 using namespace std;
51 using namespace lyx::support;
52
53 namespace lyx {
54
55 // Keep the changes documented in the Customization manual.
56 //
57 // If you change this format, then you MUST also make sure that
58 // your changes do not invalidate the hardcoded layout file in
59 // LayoutFile.cpp. Additions will never do so, but syntax changes
60 // could. See LayoutFileList::addEmptyClass() and, especially, the
61 // definition of the layoutpost string.
62 // You should also run the development/tools/updatelayouts.py script,
63 // to update the format of all of our layout files.
64 //
65 int const LAYOUT_FORMAT = 80; // spitz: Requires for floats
66
67
68 // Layout format for the current lyx file format. Controls which format is
69 // targeted by Local Layout > Convert. In master, equal to LAYOUT_FORMAT.
70 int const LYXFILE_LAYOUT_FORMAT = LAYOUT_FORMAT;
71
72
73 namespace {
74
75 class LayoutNamesEqual : public unary_function<Layout, bool> {
76 public:
77         LayoutNamesEqual(docstring const & name)
78                 : name_(name)
79         {}
80         bool operator()(Layout const & c) const
81         {
82                 return c.name() == name_;
83         }
84 private:
85         docstring name_;
86 };
87
88
89 bool layout2layout(FileName const & filename, FileName const & tempfile,
90                    int const format = LAYOUT_FORMAT)
91 {
92         FileName const script = libFileSearch("scripts", "layout2layout.py");
93         if (script.empty()) {
94                 LYXERR0("Could not find layout conversion "
95                         "script layout2layout.py.");
96                 return false;
97         }
98
99         ostringstream command;
100         command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
101                 << " -t " << format
102                 << ' ' << quoteName(filename.toFilesystemEncoding())
103                 << ' ' << quoteName(tempfile.toFilesystemEncoding());
104         string const command_str = command.str();
105
106         LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
107
108         cmd_ret const ret = runCommand(command_str);
109         if (ret.first != 0) {
110                 if (format == LAYOUT_FORMAT)
111                         LYXERR0("Conversion of layout with layout2layout.py has failed.");
112                 return false;
113         }
114         return true;
115 }
116
117
118 string translateReadType(TextClass::ReadType rt)
119 {
120         switch (rt) {
121         case TextClass::BASECLASS:
122                 return "textclass";
123         case TextClass::MERGE:
124                 return "input file";
125         case TextClass::MODULE:
126                 return "module file";
127         case TextClass::CITE_ENGINE:
128                 return "cite engine";
129         case TextClass::VALIDATION:
130                 return "validation";
131         }
132         // shutup warning
133         return string();
134 }
135
136 } // namespace
137
138
139 // This string should not be translated here,
140 // because it is a layout identifier.
141 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
142
143
144 /////////////////////////////////////////////////////////////////////////
145 //
146 // TextClass
147 //
148 /////////////////////////////////////////////////////////////////////////
149
150 TextClass::TextClass()
151         : loaded_(false), tex_class_avail_(false),
152           opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
153           opt_pagesize_("default|a4|a5|b5|letter|legal|executive"),
154           opt_pagestyle_("empty|plain|headings|fancy"), fontsize_format_("$$spt"), pagesize_("default"),
155           pagesize_format_("$$spaper"), pagestyle_("default"), tablestyle_("default"),
156           columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3), outputType_(LATEX),
157           outputFormat_("latex"), has_output_format_(false), defaultfont_(sane_font), 
158           titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
159           min_toclevel_(0), max_toclevel_(0), maxcitenames_(2),
160           cite_full_author_list_(true), bibintoc_(false)
161 {
162 }
163
164
165 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
166 {
167         LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
168         if (!lay.read(lexrc, *this)) {
169                 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
170                 return false;
171         }
172         // Resolve fonts
173         lay.resfont = lay.font;
174         lay.resfont.realize(defaultfont_);
175         lay.reslabelfont = lay.labelfont;
176         lay.reslabelfont.realize(defaultfont_);
177         return true; // no errors
178 }
179
180
181 enum TextClassTags {
182         TC_OUTPUTTYPE = 1,
183         TC_OUTPUTFORMAT,
184         TC_INPUT,
185         TC_STYLE,
186         TC_MODIFYSTYLE,
187         TC_PROVIDESTYLE,
188         TC_DEFAULTSTYLE,
189         TC_INSETLAYOUT,
190         TC_NOINSETLAYOUT,
191         TC_NOSTYLE,
192         TC_COLUMNS,
193         TC_SIDES,
194         TC_PAGESTYLE,
195         TC_PAGESIZE,
196         TC_DEFAULTFONT,
197         TC_SECNUMDEPTH,
198         TC_TOCDEPTH,
199         TC_CLASSOPTIONS,
200         TC_PREAMBLE,
201         TC_HTMLPREAMBLE,
202         TC_HTMLSTYLES,
203         TC_PROVIDES,
204         TC_REQUIRES,
205         TC_PKGOPTS,
206         TC_LEFTMARGIN,
207         TC_RIGHTMARGIN,
208         TC_FLOAT,
209         TC_COUNTER,
210         TC_NOCOUNTER,
211         TC_IFCOUNTER,
212         TC_NOFLOAT,
213         TC_TITLELATEXNAME,
214         TC_TITLELATEXTYPE,
215         TC_FORMAT,
216         TC_ADDTOPREAMBLE,
217         TC_ADDTOHTMLPREAMBLE,
218         TC_ADDTOHTMLSTYLES,
219         TC_DEFAULTMODULE,
220         TC_PROVIDESMODULE,
221         TC_EXCLUDESMODULE,
222         TC_HTMLTOCSECTION,
223         TC_CITEENGINE,
224         TC_ADDTOCITEENGINE,
225         TC_CITEENGINETYPE,
226         TC_CITEFORMAT,
227         TC_CITEFRAMEWORK,
228         TC_MAXCITENAMES,
229         TC_DEFAULTBIBLIO,
230         TC_FULLAUTHORLIST,
231         TC_OUTLINERNAME,
232         TC_TABLESTYLE,
233         TC_BIBINTOC
234 };
235
236
237 namespace {
238
239 LexerKeyword textClassTags[] = {
240         { "addtociteengine",   TC_ADDTOCITEENGINE },
241         { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
242         { "addtohtmlstyles",   TC_ADDTOHTMLSTYLES },
243         { "addtopreamble",     TC_ADDTOPREAMBLE },
244         { "bibintoc",          TC_BIBINTOC },
245         { "citeengine",        TC_CITEENGINE },
246         { "citeenginetype",    TC_CITEENGINETYPE },
247         { "citeformat",        TC_CITEFORMAT },
248         { "citeframework",     TC_CITEFRAMEWORK },
249         { "classoptions",      TC_CLASSOPTIONS },
250         { "columns",           TC_COLUMNS },
251         { "counter",           TC_COUNTER },
252         { "defaultbiblio",     TC_DEFAULTBIBLIO },
253         { "defaultfont",       TC_DEFAULTFONT },
254         { "defaultmodule",     TC_DEFAULTMODULE },
255         { "defaultstyle",      TC_DEFAULTSTYLE },
256         { "excludesmodule",    TC_EXCLUDESMODULE },
257         { "float",             TC_FLOAT },
258         { "format",            TC_FORMAT },
259         { "fullauthorlist",    TC_FULLAUTHORLIST },
260         { "htmlpreamble",      TC_HTMLPREAMBLE },
261         { "htmlstyles",        TC_HTMLSTYLES },
262         { "htmltocsection",    TC_HTMLTOCSECTION },
263         { "ifcounter",         TC_IFCOUNTER },
264         { "input",             TC_INPUT },
265         { "insetlayout",       TC_INSETLAYOUT },
266         { "leftmargin",        TC_LEFTMARGIN },
267         { "maxcitenames",      TC_MAXCITENAMES },
268         { "modifystyle",       TC_MODIFYSTYLE },
269         { "nocounter",         TC_NOCOUNTER },
270         { "nofloat",           TC_NOFLOAT },
271         { "noinsetlayout",     TC_NOINSETLAYOUT },
272         { "nostyle",           TC_NOSTYLE },
273         { "outlinername",      TC_OUTLINERNAME },
274         { "outputformat",      TC_OUTPUTFORMAT },
275         { "outputtype",        TC_OUTPUTTYPE },
276         { "packageoptions",    TC_PKGOPTS },
277         { "pagesize",          TC_PAGESIZE },
278         { "pagestyle",         TC_PAGESTYLE },
279         { "preamble",          TC_PREAMBLE },
280         { "provides",          TC_PROVIDES },
281         { "providesmodule",    TC_PROVIDESMODULE },
282         { "providestyle",      TC_PROVIDESTYLE },
283         { "requires",          TC_REQUIRES },
284         { "rightmargin",       TC_RIGHTMARGIN },
285         { "secnumdepth",       TC_SECNUMDEPTH },
286         { "sides",             TC_SIDES },
287         { "style",             TC_STYLE },
288         { "tablestyle",        TC_TABLESTYLE },
289         { "titlelatexname",    TC_TITLELATEXNAME },
290         { "titlelatextype",    TC_TITLELATEXTYPE },
291         { "tocdepth",          TC_TOCDEPTH }
292 };
293
294 } // namespace
295
296
297 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
298 {
299         LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
300         TempFile tmp("convertXXXXXX.layout");
301         FileName const tempfile = tmp.name();
302         bool success = layout2layout(filename, tempfile);
303         if (success)
304                 success = readWithoutConv(tempfile, rt) == OK;
305         return success;
306 }
307
308
309 std::string TextClass::convert(std::string const & str)
310 {
311         TempFile tmp1("localXXXXXX.layout");
312         FileName const fn = tmp1.name();
313         ofstream os(fn.toFilesystemEncoding().c_str());
314         os << str;
315         os.close();
316         TempFile tmp2("convert_localXXXXXX.layout");
317         FileName const tempfile = tmp2.name();
318         bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
319         if (!success)
320                 return "";
321         ifstream is(tempfile.toFilesystemEncoding().c_str());
322         string ret;
323         string tmp;
324         while (!is.eof()) {
325                 getline(is, tmp);
326                 ret += tmp + '\n';
327         }
328         is.close();
329         return ret;
330 }
331
332
333 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
334 {
335         if (!filename.isReadableFile()) {
336                 lyxerr << "Cannot read layout file `" << filename << "'."
337                        << endl;
338                 return ERROR;
339         }
340
341         LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
342                 to_utf8(makeDisplayPath(filename.absFileName())));
343
344         // Define the plain layout used in table cells, ert, etc. Note that
345         // we do this before loading any layout file, so that classes can
346         // override features of this layout if they should choose to do so.
347         if (rt == BASECLASS && !hasLayout(plain_layout_))
348                 layoutlist_.push_back(createBasicLayout(plain_layout_));
349
350         Lexer lexrc(textClassTags);
351         lexrc.setFile(filename);
352         ReturnValues retval = read(lexrc, rt);
353
354         LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
355                         to_utf8(makeDisplayPath(filename.absFileName())));
356
357         return retval;
358 }
359
360
361 bool TextClass::read(FileName const & filename, ReadType rt)
362 {
363         ReturnValues const retval = readWithoutConv(filename, rt);
364         if (retval != FORMAT_MISMATCH)
365                 return retval == OK;
366
367         bool const worx = convertLayoutFormat(filename, rt);
368         if (!worx)
369                 LYXERR0 ("Unable to convert " << filename <<
370                         " to format " << LAYOUT_FORMAT);
371         return worx;
372 }
373
374
375 TextClass::ReturnValues TextClass::validate(std::string const & str)
376 {
377         TextClass tc;
378         return tc.read(str, VALIDATION);
379 }
380
381
382 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
383 {
384         Lexer lexrc(textClassTags);
385         istringstream is(str);
386         lexrc.setStream(is);
387         ReturnValues retval = read(lexrc, rt);
388
389         if (retval != FORMAT_MISMATCH)
390                 return retval;
391
392         // write the layout string to a temporary file
393         TempFile tmp("TextClass_read");
394         FileName const tempfile = tmp.name();
395         ofstream os(tempfile.toFilesystemEncoding().c_str());
396         if (!os) {
397                 LYXERR0("Unable to create temporary file");
398                 return ERROR;
399         }
400         os << str;
401         os.close();
402
403         // now try to convert it to LAYOUT_FORMAT
404         if (!convertLayoutFormat(tempfile, rt)) {
405                 LYXERR0("Unable to convert internal layout information to format "
406                         << LAYOUT_FORMAT);
407                 return ERROR;
408         }
409
410         return OK_OLDFORMAT;
411 }
412
413
414 // Reads a textclass structure from file.
415 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
416 {
417         if (!lexrc.isOK())
418                 return ERROR;
419
420         // The first usable line should be
421         // Format LAYOUT_FORMAT
422         if (lexrc.lex() != TC_FORMAT || !lexrc.next()
423             || lexrc.getInteger() != LAYOUT_FORMAT)
424                 return FORMAT_MISMATCH;
425
426         // parsing
427         bool error = false;
428         while (lexrc.isOK() && !error) {
429                 int le = lexrc.lex();
430
431                 switch (le) {
432                 case Lexer::LEX_FEOF:
433                         continue;
434
435                 case Lexer::LEX_UNDEF:
436                         lexrc.printError("Unknown TextClass tag `$$Token'");
437                         error = true;
438                         continue;
439
440                 default:
441                         break;
442                 }
443
444                 // used below to track whether we are in an IfStyle or IfCounter tag.
445                 bool modifystyle  = false;
446                 bool providestyle = false;
447                 bool ifcounter    = false;
448
449                 switch (static_cast<TextClassTags>(le)) {
450
451                 case TC_FORMAT:
452                         lexrc.next();
453                         lexrc.printError("Duplicate Format directive");
454                         break;
455
456                 case TC_OUTPUTFORMAT:
457                         if (lexrc.next()) {
458                                 outputFormat_ = lexrc.getString();
459                                 has_output_format_ = true;
460                         }
461                         break;
462
463                 case TC_OUTPUTTYPE:
464                         readOutputType(lexrc);
465                         switch(outputType_) {
466                         case LATEX:
467                                 outputFormat_ = "latex";
468                                 break;
469                         case DOCBOOK:
470                                 outputFormat_ = "docbook";
471                                 break;
472                         case LITERATE:
473                                 outputFormat_ = "literate";
474                                 break;
475                         }
476                         break;
477
478                 case TC_INPUT: // Include file
479                         if (lexrc.next()) {
480                                 FileName tmp;
481                                 string const inc = lexrc.getString();
482                                 if (!path().empty() && (prefixIs(inc, "./") ||
483                                                         prefixIs(inc, "../")))
484                                         tmp = fileSearch(path(), inc, "layout");
485                                 else
486                                         tmp = libFileSearch("layouts", inc,
487                                                             "layout");
488
489                                 if (tmp.empty()) {
490                                         lexrc.printError("Could not find input file: " + inc);
491                                         error = true;
492                                 } else if (!read(tmp, MERGE)) {
493                                         lexrc.printError("Error reading input file: " + tmp.absFileName());
494                                         error = true;
495                                 }
496                         }
497                         break;
498
499                 case TC_DEFAULTSTYLE:
500                         if (lexrc.next()) {
501                                 docstring const name = from_utf8(subst(lexrc.getString(),
502                                                           '_', ' '));
503                                 defaultlayout_ = name;
504                         }
505                         break;
506
507                 case TC_MODIFYSTYLE:
508                         modifystyle = true;
509                 // fall through
510                 case TC_PROVIDESTYLE:
511                         // if modifystyle is true, then we got here by falling through
512                         // so we are not in an ProvideStyle block
513                         if (!modifystyle)
514                                 providestyle = true;
515                 // fall through
516                 case TC_STYLE: {
517                         if (!lexrc.next()) {
518                                 lexrc.printError("No name given for style: `$$Token'.");
519                                 error = true;
520                                 break;
521                         }
522                         docstring const name = from_utf8(subst(lexrc.getString(),
523                                                         '_', ' '));
524                         if (name.empty()) {
525                                 string s = "Could not read name for style: `$$Token' "
526                                         + lexrc.getString() + " is probably not valid UTF-8!";
527                                 lexrc.printError(s);
528                                 Layout lay;
529                                 // Since we couldn't read the name, we just scan the rest
530                                 // of the style and discard it.
531                                 error = !readStyle(lexrc, lay);
532                                 break;
533                         }
534
535                         bool const have_layout = hasLayout(name);
536
537                         // If the layout already exists, then we want to add it to
538                         // the existing layout, as long as we are not in an ProvideStyle
539                         // block.
540                         if (have_layout && !providestyle) {
541                                 Layout & lay = operator[](name);
542                                 error = !readStyle(lexrc, lay);
543                         }
544                         // If the layout does not exist, then we want to create a new
545                         // one, but not if we are in a ModifyStyle block.
546                         else if (!have_layout && !modifystyle) {
547                                 Layout layout;
548                                 layout.setName(name);
549                                 error = !readStyle(lexrc, layout);
550                                 if (!error)
551                                         layoutlist_.push_back(layout);
552
553                                 if (defaultlayout_.empty()) {
554                                         // We do not have a default layout yet, so we choose
555                                         // the first layout we encounter.
556                                         defaultlayout_ = name;
557                                 }
558                         }
559                         // There are two ways to get here:
560                         //  (i)  The layout exists but we are in an ProvideStyle block
561                         //  (ii) The layout doesn't exist, but we are in an ModifyStyle
562                         //       block.
563                         // Either way, we just scan the rest and discard it
564                         else {
565                                 Layout lay;
566                                 // signal to coverity that we do not care about the result
567                                 (void)readStyle(lexrc, lay);
568                         }
569                         break;
570                 }
571
572                 case TC_NOSTYLE:
573                         if (lexrc.next()) {
574                                 docstring const style = from_utf8(subst(lexrc.getString(),
575                                                      '_', ' '));
576                                 if (!deleteLayout(style))
577                                         lyxerr << "Cannot delete style `"
578                                                << to_utf8(style) << '\'' << endl;
579                         }
580                         break;
581
582                 case TC_NOINSETLAYOUT:
583                         if (lexrc.next()) {
584                                 docstring const style = from_utf8(subst(lexrc.getString(),
585                                                                  '_', ' '));
586                                 if (!deleteInsetLayout(style))
587                                         LYXERR0("Style `" << style << "' cannot be removed\n"
588                                                 "because it was not found!");
589                         }
590                         break;
591
592                 case TC_COLUMNS:
593                         if (lexrc.next())
594                                 columns_ = lexrc.getInteger();
595                         break;
596
597                 case TC_SIDES:
598                         if (lexrc.next()) {
599                                 switch (lexrc.getInteger()) {
600                                 case 1: sides_ = OneSide; break;
601                                 case 2: sides_ = TwoSides; break;
602                                 default:
603                                         lyxerr << "Impossible number of page"
604                                                 " sides, setting to one."
605                                                << endl;
606                                         sides_ = OneSide;
607                                         break;
608                                 }
609                         }
610                         break;
611
612                 case TC_PAGESIZE:
613                         lexrc.next();
614                         pagesize_ = rtrim(lexrc.getString());
615                         break;
616
617                 case TC_PAGESTYLE:
618                         lexrc.next();
619                         pagestyle_ = rtrim(lexrc.getString());
620                         break;
621
622                 case TC_DEFAULTFONT:
623                         defaultfont_ = lyxRead(lexrc);
624                         if (!defaultfont_.resolved()) {
625                                 lexrc.printError("Warning: defaultfont should "
626                                                  "be fully instantiated!");
627                                 defaultfont_.realize(sane_font);
628                         }
629                         break;
630
631                 case TC_SECNUMDEPTH:
632                         lexrc.next();
633                         secnumdepth_ = lexrc.getInteger();
634                         break;
635
636                 case TC_TOCDEPTH:
637                         lexrc.next();
638                         tocdepth_ = lexrc.getInteger();
639                         break;
640
641                 // First step to support options
642                 case TC_CLASSOPTIONS:
643                         readClassOptions(lexrc);
644                         break;
645
646                 case TC_PREAMBLE:
647                         preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
648                         break;
649
650                 case TC_HTMLPREAMBLE:
651                         htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
652                         break;
653
654                 case TC_HTMLSTYLES:
655                         htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
656                         break;
657
658                 case TC_HTMLTOCSECTION:
659                         html_toc_section_ = from_utf8(trim(lexrc.getString()));
660                         break;
661
662                 case TC_ADDTOPREAMBLE:
663                         preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
664                         break;
665
666                 case TC_ADDTOHTMLPREAMBLE:
667                         htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
668                         break;
669
670                 case TC_ADDTOHTMLSTYLES:
671                         htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
672                         break;
673
674                 case TC_PROVIDES: {
675                         lexrc.next();
676                         string const feature = lexrc.getString();
677                         lexrc.next();
678                         if (lexrc.getInteger())
679                                 provides_.insert(feature);
680                         else
681                                 provides_.erase(feature);
682                         break;
683                 }
684
685                 case TC_REQUIRES: {
686                         lexrc.eatLine();
687                         vector<string> const req
688                                 = getVectorFromString(lexrc.getString());
689                         requires_.insert(req.begin(), req.end());
690                         break;
691                 }
692
693                 case TC_PKGOPTS : {
694                         lexrc.next();
695                         string const pkg = lexrc.getString();
696                         lexrc.next();
697                         string const options = lexrc.getString();
698                         package_options_[pkg] = options;
699                         break;
700                 }
701
702                 case TC_DEFAULTMODULE: {
703                         lexrc.next();
704                         string const module = lexrc.getString();
705                         if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
706                                 default_modules_.push_back(module);
707                         break;
708                 }
709
710                 case TC_PROVIDESMODULE: {
711                         lexrc.next();
712                         string const module = lexrc.getString();
713                         if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
714                                 provided_modules_.push_back(module);
715                         break;
716                 }
717
718                 case TC_EXCLUDESMODULE: {
719                         lexrc.next();
720                         string const module = lexrc.getString();
721                         // modules already have their own way to exclude other modules
722                         if (rt == MODULE) {
723                                 LYXERR0("ExcludesModule tag cannot be used in a module!");
724                                 break;
725                         }
726                         if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
727                                 excluded_modules_.push_back(module);
728                         break;
729                 }
730
731                 case TC_LEFTMARGIN:     // left margin type
732                         if (lexrc.next())
733                                 leftmargin_ = lexrc.getDocString();
734                         break;
735
736                 case TC_RIGHTMARGIN:    // right margin type
737                         if (lexrc.next())
738                                 rightmargin_ = lexrc.getDocString();
739                         break;
740
741                 case TC_INSETLAYOUT: {
742                         if (!lexrc.next()) {
743                                 lexrc.printError("No name given for InsetLayout: `$$Token'.");
744                                 error = true;
745                                 break;
746                         }
747                         docstring const name = subst(lexrc.getDocString(), '_', ' ');
748                         bool const validating = (rt == VALIDATION);
749                         if (name.empty()) {
750                                 string s = "Could not read name for InsetLayout: `$$Token' "
751                                         + lexrc.getString() + " is probably not valid UTF-8!";
752                                 lexrc.printError(s);
753                                 InsetLayout il;
754                                 // Since we couldn't read the name, we just scan the rest
755                                 // of the style and discard it.
756                                 il.read(lexrc, *this);
757                                 // Let's try to continue rather than abort, unless we're validating
758                                 // in which case we want to report the error
759                                 if (validating)
760                                         error = true;
761                         } else if (hasInsetLayout(name)) {
762                                 InsetLayout & il = insetlayoutlist_[name];
763                                 error = !il.read(lexrc, *this, validating);
764                         } else {
765                                 InsetLayout il;
766                                 il.setName(name);
767                                 error = !il.read(lexrc, *this, validating);
768                                 if (!error)
769                                         insetlayoutlist_[name] = il;
770                         }
771                         break;
772                 }
773
774                 case TC_FLOAT:
775                         error = !readFloat(lexrc);
776                         break;
777
778                 case TC_CITEENGINE:
779                         error = !readCiteEngine(lexrc, rt);
780                         break;
781
782                 case TC_ADDTOCITEENGINE:
783                         error = !readCiteEngine(lexrc, rt, true);
784                         break;
785
786                 case TC_CITEENGINETYPE:
787                         if (lexrc.next())
788                                 opt_enginetype_ = rtrim(lexrc.getString());
789                         break;
790
791                 case TC_CITEFORMAT:
792                         error = !readCiteFormat(lexrc, rt);
793                         break;
794
795                 case TC_CITEFRAMEWORK:
796                         lexrc.next();
797                         citeframework_ = rtrim(lexrc.getString());
798                         break;
799
800                 case TC_MAXCITENAMES:
801                         lexrc.next();
802                         maxcitenames_ = size_t(lexrc.getInteger());
803                         break;
804
805                 case TC_DEFAULTBIBLIO:
806                         if (lexrc.next()) {
807                                 vector<string> const dbs =
808                                         getVectorFromString(rtrim(lexrc.getString()), "|");
809                                 vector<string>::const_iterator it  = dbs.begin();
810                                 vector<string>::const_iterator end = dbs.end();
811                                 for (; it != end; ++it) {
812                                         if (!contains(*it, ':')) {
813                                                 vector<string> const enginetypes =
814                                                         getVectorFromString(opt_enginetype_, "|");
815                                                 for (string const &s: enginetypes)
816                                                         cite_default_biblio_style_[s] = *it;
817                                         } else {
818                                                 string eng;
819                                                 string const db = split(*it, eng, ':');
820                                                 cite_default_biblio_style_[eng] = db;
821                                         }
822                                 }
823                         }
824                         break;
825
826                 case TC_BIBINTOC:
827                         if (lexrc.next())
828                                 bibintoc_ = lexrc.getBool();
829                         break;
830
831                 case TC_FULLAUTHORLIST:
832                         if (lexrc.next())
833                                 cite_full_author_list_ &= lexrc.getBool();
834                         break;
835
836                 case TC_NOCOUNTER:
837                         if (lexrc.next()) {
838                                 docstring const cnt = lexrc.getDocString();
839                                 if (!counters_.remove(cnt))
840                                         LYXERR0("Unable to remove counter: " + to_utf8(cnt));
841                         }
842                         break;
843
844                 case TC_IFCOUNTER:
845                         ifcounter = true;
846                         // fall through
847                 case TC_COUNTER:
848                         if (lexrc.next()) {
849                                 docstring const name = lexrc.getDocString();
850                                 if (name.empty()) {
851                                         string s = "Could not read name for counter: `$$Token' "
852                                                         + lexrc.getString() + " is probably not valid UTF-8!";
853                                         lexrc.printError(s.c_str());
854                                         Counter c;
855                                         // Since we couldn't read the name, we just scan the rest
856                                         // and discard it.
857                                         c.read(lexrc);
858                                 } else
859                                         error = !counters_.read(lexrc, name, !ifcounter);
860                         }
861                         else {
862                                 lexrc.printError("No name given for style: `$$Token'.");
863                                 error = true;
864                         }
865                         break;
866
867                 case TC_TITLELATEXTYPE:
868                         readTitleType(lexrc);
869                         break;
870
871                 case TC_TITLELATEXNAME:
872                         if (lexrc.next())
873                                 titlename_ = lexrc.getString();
874                         break;
875
876                 case TC_NOFLOAT:
877                         if (lexrc.next()) {
878                                 string const nofloat = lexrc.getString();
879                                 floatlist_.erase(nofloat);
880                         }
881                         break;
882
883                 case TC_OUTLINERNAME:
884                         error = !readOutlinerName(lexrc);
885                         break;
886
887                 case TC_TABLESTYLE:
888                         lexrc.next();
889                         tablestyle_ = rtrim(lexrc.getString());
890                         break;
891                 } // end of switch
892         }
893
894         // at present, we abort if we encounter an error,
895         // so there is no point continuing.
896         if (error)
897                 return ERROR;
898
899         if (rt != BASECLASS)
900                 return OK;
901
902         if (defaultlayout_.empty()) {
903                 LYXERR0("Error: Textclass '" << name_
904                                                 << "' is missing a defaultstyle.");
905                 return ERROR;
906         }
907
908         // Try to erase "stdinsets" from the provides_ set.
909         // The
910         //   Provides stdinsets 1
911         // declaration simply tells us that the standard insets have been
912         // defined. (It's found in stdinsets.inc but could also be used in
913         // user-defined files.) There isn't really any such package. So we
914         // might as well go ahead and erase it.
915         // If we do not succeed, then it was not there, which means that
916         // the textclass did not provide the definitions of the standard
917         // insets. So we need to try to load them.
918         int erased = provides_.erase("stdinsets");
919         if (!erased) {
920                 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
921
922                 if (tmp.empty()) {
923                         frontend::Alert::warning(_("Missing File"),
924                                 _("Could not find stdinsets.inc! This may lead to data loss!"));
925                         error = true;
926                 } else if (!read(tmp, MERGE)) {
927                         frontend::Alert::warning(_("Corrupt File"),
928                                 _("Could not read stdinsets.inc! This may lead to data loss!"));
929                         error = true;
930                 }
931         }
932
933         min_toclevel_ = Layout::NOT_IN_TOC;
934         max_toclevel_ = Layout::NOT_IN_TOC;
935         const_iterator lit = begin();
936         const_iterator len = end();
937         for (; lit != len; ++lit) {
938                 int const toclevel = lit->toclevel;
939                 if (toclevel != Layout::NOT_IN_TOC) {
940                         if (min_toclevel_ == Layout::NOT_IN_TOC)
941                                 min_toclevel_ = toclevel;
942                         else
943                                 min_toclevel_ = min(min_toclevel_, toclevel);
944                         max_toclevel_ = max(max_toclevel_, toclevel);
945                 }
946         }
947         LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
948                 << ", maximum is " << max_toclevel_);
949
950         return (error ? ERROR : OK);
951 }
952
953
954 void TextClass::readTitleType(Lexer & lexrc)
955 {
956         LexerKeyword titleTypeTags[] = {
957                 { "commandafter", TITLE_COMMAND_AFTER },
958                 { "environment",  TITLE_ENVIRONMENT }
959         };
960
961         PushPopHelper pph(lexrc, titleTypeTags);
962
963         int le = lexrc.lex();
964         switch (le) {
965         case Lexer::LEX_UNDEF:
966                 lexrc.printError("Unknown output type `$$Token'");
967                 break;
968         case TITLE_COMMAND_AFTER:
969         case TITLE_ENVIRONMENT:
970                 titletype_ = static_cast<TitleLatexType>(le);
971                 break;
972         default:
973                 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
974                 break;
975         }
976 }
977
978
979 void TextClass::readOutputType(Lexer & lexrc)
980 {
981         LexerKeyword outputTypeTags[] = {
982                 { "docbook",  DOCBOOK },
983                 { "latex",    LATEX },
984                 { "literate", LITERATE }
985         };
986
987         PushPopHelper pph(lexrc, outputTypeTags);
988
989         int le = lexrc.lex();
990         switch (le) {
991         case Lexer::LEX_UNDEF:
992                 lexrc.printError("Unknown output type `$$Token'");
993                 return;
994         case LATEX:
995         case DOCBOOK:
996         case LITERATE:
997                 outputType_ = static_cast<OutputType>(le);
998                 break;
999         default:
1000                 LYXERR0("Unhandled value " << le);
1001                 break;
1002         }
1003 }
1004
1005
1006 void TextClass::readClassOptions(Lexer & lexrc)
1007 {
1008         enum {
1009                 CO_FONTSIZE = 1,
1010                 CO_FONTSIZE_FORMAT,
1011                 CO_PAGESIZE,
1012                 CO_PAGESIZE_FORMAT,
1013                 CO_PAGESTYLE,
1014                 CO_OTHER,
1015                 CO_HEADER,
1016                 CO_END
1017         };
1018
1019         LexerKeyword classOptionsTags[] = {
1020                 {"end",       CO_END },
1021                 {"fontsize",  CO_FONTSIZE },
1022                 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1023                 {"header",    CO_HEADER },
1024                 {"other",     CO_OTHER },
1025                 {"pagesize",  CO_PAGESIZE },
1026                 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1027                 {"pagestyle", CO_PAGESTYLE }
1028         };
1029
1030         lexrc.pushTable(classOptionsTags);
1031         bool getout = false;
1032         while (!getout && lexrc.isOK()) {
1033                 int le = lexrc.lex();
1034                 switch (le) {
1035                 case Lexer::LEX_UNDEF:
1036                         lexrc.printError("Unknown ClassOption tag `$$Token'");
1037                         continue;
1038                 default:
1039                         break;
1040                 }
1041                 switch (le) {
1042                 case CO_FONTSIZE:
1043                         lexrc.next();
1044                         opt_fontsize_ = rtrim(lexrc.getString());
1045                         break;
1046                 case CO_FONTSIZE_FORMAT:
1047                         lexrc.next();
1048                         fontsize_format_ = rtrim(lexrc.getString());
1049                         break;
1050                 case CO_PAGESIZE:
1051                         lexrc.next();
1052                         opt_pagesize_ = rtrim(lexrc.getString());
1053                         break;
1054                 case CO_PAGESIZE_FORMAT:
1055                         lexrc.next();
1056                         pagesize_format_ = rtrim(lexrc.getString());
1057                         break;
1058                 case CO_PAGESTYLE:
1059                         lexrc.next();
1060                         opt_pagestyle_ = rtrim(lexrc.getString());
1061                         break;
1062                 case CO_OTHER:
1063                         lexrc.next();
1064                         if (options_.empty())
1065                                 options_ = lexrc.getString();
1066                         else
1067                                 options_ += ',' + lexrc.getString();
1068                         break;
1069                 case CO_HEADER:
1070                         lexrc.next();
1071                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
1072                         break;
1073                 case CO_END:
1074                         getout = true;
1075                         break;
1076                 }
1077         }
1078         lexrc.popTable();
1079 }
1080
1081
1082 vector<CitationStyle> const & TextClass::getCiteStyles(
1083         CiteEngineType const & type) const
1084 {
1085         static vector<CitationStyle> empty;
1086         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1087         if (it == cite_styles_.end())
1088                 return empty;
1089         return it->second;
1090 }
1091
1092
1093 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1094 {
1095         int const type = readCiteEngineType(lexrc);
1096         bool authoryear = (type & ENGINE_TYPE_AUTHORYEAR);
1097         bool numerical = (type & ENGINE_TYPE_NUMERICAL);
1098         bool defce = (type & ENGINE_TYPE_DEFAULT);
1099
1100         if (rt == CITE_ENGINE) {
1101                 // The cite engines are not supposed to overwrite
1102                 // CiteStyle defined by the class or a module.
1103                 if (authoryear)
1104                         authoryear = getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1105                 if (numerical)
1106                         numerical = getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1107                 if (defce)
1108                         defce = getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1109         }
1110
1111         if (rt != CITE_ENGINE && !add) {
1112                 // Reset if we defined CiteStyle
1113                 // from the class or a module
1114                 if (authoryear)
1115                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1116                 if (numerical)
1117                         cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1118                 if (defce)
1119                         cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1120         }
1121
1122         string def;
1123         bool getout = false;
1124         while (!getout && lexrc.isOK()) {
1125                 lexrc.eatLine();
1126                 def = lexrc.getString();
1127                 def = subst(def, " ", "");
1128                 def = subst(def, "\t", "");
1129                 if (compare_ascii_no_case(def, "end") == 0) {
1130                         getout = true;
1131                         continue;
1132                 }
1133                 CitationStyle cs;
1134                 char ichar = def[0];
1135                 if (ichar == '#')
1136                         continue;
1137                 if (isUpperCase(ichar)) {
1138                         cs.forceUpperCase = true;
1139                         def[0] = lowercase(ichar);
1140                 }
1141
1142                 /** For portability reasons (between different
1143                  *  cite engines such as natbib and biblatex),
1144                  *  we distinguish between:
1145                  *  1. The LyX name as output in the LyX file
1146                  *  2. Possible aliases that might fall back to
1147                  *     the given LyX name in the current engine
1148                  *  3. The actual LaTeX command that is output
1149                  *  (2) and (3) are optional.
1150                  *  Also, the GUI string for the starred version can
1151                  *  be changed
1152                  *  The syntax is:
1153                  *  LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1154                  */
1155                 enum ScanMode {
1156                         LyXName,
1157                         Alias,
1158                         LaTeXCmd,
1159                         StarDesc
1160                 };
1161
1162                 ScanMode mode = LyXName;
1163                 ScanMode oldmode = LyXName;
1164                 string lyx_cmd;
1165                 string alias;
1166                 string latex_cmd;
1167                 string stardesc;
1168                 size_t const n = def.size();
1169                 for (size_t i = 0; i != n; ++i) {
1170                         ichar = def[i];
1171                         if (ichar == '|')
1172                                 mode = Alias;
1173                         else if (ichar == '=')
1174                                 mode = LaTeXCmd;
1175                         else if (ichar == '<') {
1176                                 oldmode = mode;
1177                                 mode = StarDesc;
1178                         } else if (ichar == '>')
1179                                 mode = oldmode;
1180                         else if (mode == LaTeXCmd)
1181                                 latex_cmd += ichar;
1182                         else if (mode == StarDesc)
1183                                 stardesc += ichar;
1184                         else if (ichar == '$')
1185                                 cs.hasQualifiedList = true;
1186                         else if (ichar == '*')
1187                                 cs.hasStarredVersion = true;
1188                         else if (ichar == '[' && cs.textAfter)
1189                                 cs.textBefore = true;
1190                         else if (ichar == '[')
1191                                 cs.textAfter = true;
1192                         else if (ichar != ']') {
1193                                 if (mode == Alias)
1194                                         alias += ichar;
1195                                 else
1196                                         lyx_cmd += ichar;
1197                         }
1198                 }
1199                 cs.name = lyx_cmd;
1200                 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1201                 if (!alias.empty()) {
1202                         vector<string> const aliases = getVectorFromString(alias);
1203                         for (string const &s: aliases)
1204                                 cite_command_aliases_[s] = lyx_cmd;
1205                 }
1206                 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1207                 int size = int(stardesc.size());
1208                 if (size > 0)
1209                         cs.stardesc = stardescs[0];
1210                 if (size > 1)
1211                         cs.startooltip = stardescs[1];
1212                 if (add) {
1213                         if (authoryear)
1214                                 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1215                         if (numerical)
1216                                 class_cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1217                         if (defce)
1218                                 class_cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1219                 } else {
1220                         if (authoryear)
1221                                 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1222                         if (numerical)
1223                                 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1224                         if (defce)
1225                                 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1226                 }
1227         }
1228         // If we do AddToCiteEngine, do not apply yet,
1229         // except if we have already a style to add something to
1230         bool apply_ay = !add;
1231         bool apply_num = !add;
1232         bool apply_def = !add;
1233         if (add) {
1234                 if (type & ENGINE_TYPE_AUTHORYEAR)
1235                         apply_ay = !getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1236                 if (type & ENGINE_TYPE_NUMERICAL)
1237                         apply_num = !getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1238                 if (type & ENGINE_TYPE_DEFAULT)
1239                         apply_def = !getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1240         }
1241
1242         // Add the styles from AddToCiteEngine to the class' styles
1243         // (but only if they are not yet defined)
1244         for (auto const & cis : class_cite_styles_) {
1245                 // Only consider the current CiteEngineType
1246                 if (!(type & cis.first))
1247                         continue;
1248                 for (auto const & ciss : cis.second) {
1249                         bool defined = false;
1250                         // Check if the style "name" is already def'ed
1251                         for (auto const & av : getCiteStyles(cis.first))
1252                                 if (av.name == ciss.name)
1253                                         defined = true;
1254                         if (!defined) {
1255                                 if (cis.first == ENGINE_TYPE_AUTHORYEAR && apply_ay)
1256                                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(ciss);
1257                                 else if (cis.first == ENGINE_TYPE_NUMERICAL && apply_num)
1258                                         cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(ciss);
1259                                 else if (cis.first == ENGINE_TYPE_DEFAULT && apply_def)
1260                                         cite_styles_[ENGINE_TYPE_DEFAULT].push_back(ciss);
1261                         }
1262                 }
1263         }
1264         if (type & ENGINE_TYPE_AUTHORYEAR && apply_ay)
1265                 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1266         if (type & ENGINE_TYPE_NUMERICAL && apply_num)
1267                 class_cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1268         if (type & ENGINE_TYPE_DEFAULT && apply_def)
1269                 class_cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1270         return getout;
1271 }
1272
1273
1274 int TextClass::readCiteEngineType(Lexer & lexrc) const
1275 {
1276         static_assert(ENGINE_TYPE_DEFAULT ==
1277                                   (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1278                                   "Incorrect default engine type");
1279         if (!lexrc.next()) {
1280                 lexrc.printError("No cite engine type given for token: `$$Token'.");
1281                 return ENGINE_TYPE_DEFAULT;
1282         }
1283         string const type = rtrim(lexrc.getString());
1284         if (compare_ascii_no_case(type, "authoryear") == 0)
1285                 return ENGINE_TYPE_AUTHORYEAR;
1286         else if (compare_ascii_no_case(type, "numerical") == 0)
1287                 return ENGINE_TYPE_NUMERICAL;
1288         else if (compare_ascii_no_case(type, "default") != 0) {
1289                 string const s = "Unknown cite engine type `" + type
1290                         + "' given for token: `$$Token',";
1291                 lexrc.printError(s);
1292         }
1293         return ENGINE_TYPE_DEFAULT;
1294 }
1295
1296
1297 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1298 {
1299         int const type = readCiteEngineType(lexrc);
1300         string etype;
1301         string definition;
1302         // Cite engine definitions do not overwrite existing
1303         // definitions from the class or a module
1304         bool const overwrite = rt != CITE_ENGINE;
1305         while (lexrc.isOK()) {
1306                 lexrc.next();
1307                 etype = lexrc.getString();
1308                 if (compare_ascii_no_case(etype, "end") == 0)
1309                         break;
1310                 if (!lexrc.isOK())
1311                         return false;
1312                 lexrc.eatLine();
1313                 definition = lexrc.getString();
1314                 char initchar = etype[0];
1315                 if (initchar == '#')
1316                         continue;
1317                 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1318                         bool defined = false;
1319                         bool aydefined = false;
1320                         bool numdefined = false;
1321                         // Check if the macro is already def'ed
1322                         for (auto const & cm : cite_macros_) {
1323                                 if (!(type & cm.first))
1324                                         continue;
1325                                 if (cm.second.find(etype) != cm.second.end()) {
1326                                         if (type == cm.first)
1327                                                 // defined as default or specific type
1328                                                 defined = true;
1329                                         if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1330                                                 // defined for author-year
1331                                                 aydefined = true;
1332                                         else if (cm.first == ENGINE_TYPE_NUMERICAL)
1333                                                 // defined for numerical
1334                                                 numdefined = true;
1335                                 }
1336                         }
1337                         if (!defined || overwrite) {
1338                                 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1339                                         cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1340                                 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1341                                         cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1342                                 if (type == ENGINE_TYPE_DEFAULT)
1343                                         cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1344                         }
1345                 } else {
1346                         bool defined = false;
1347                         bool aydefined = false;
1348                         bool numdefined = false;
1349                         // Check if the format is already def'ed
1350                         for (auto const & cm : cite_formats_) {
1351                                 if (!(type & cm.first))
1352                                         continue;
1353                                 if (cm.second.find(etype) != cm.second.end()) {
1354                                         if (type == cm.first)
1355                                                 // defined as default or specific type
1356                                                 defined = true;
1357                                         if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1358                                                 // defined for author-year
1359                                                 aydefined = true;
1360                                         else if (cm.first == ENGINE_TYPE_NUMERICAL)
1361                                                 // defined for numerical
1362                                                 numdefined = true;
1363                                 }
1364                         }
1365                         if (!defined || overwrite){
1366                                 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1367                                         cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1368                                 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1369                                         cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1370                                 if (type == ENGINE_TYPE_DEFAULT)
1371                                         cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1372                         }
1373                 }
1374         }
1375         return true;
1376 }
1377
1378
1379 bool TextClass::readFloat(Lexer & lexrc)
1380 {
1381         enum {
1382                 FT_TYPE = 1,
1383                 FT_NAME,
1384                 FT_PLACEMENT,
1385                 FT_EXT,
1386                 FT_WITHIN,
1387                 FT_STYLE,
1388                 FT_LISTNAME,
1389                 FT_USESFLOAT,
1390                 FT_PREDEFINED,
1391                 FT_HTMLSTYLE,
1392                 FT_HTMLATTR,
1393                 FT_HTMLTAG,
1394                 FT_LISTCOMMAND,
1395                 FT_REFPREFIX,
1396                 FT_ALLOWED_PLACEMENT,
1397                 FT_ALLOWS_SIDEWAYS,
1398                 FT_ALLOWS_WIDE,
1399                 FT_REQUIRES,
1400                 FT_END
1401         };
1402
1403         LexerKeyword floatTags[] = {
1404                 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1405                 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1406                 { "allowswide", FT_ALLOWS_WIDE },
1407                 { "end", FT_END },
1408                 { "extension", FT_EXT },
1409                 { "guiname", FT_NAME },
1410                 { "htmlattr", FT_HTMLATTR },
1411                 { "htmlstyle", FT_HTMLSTYLE },
1412                 { "htmltag", FT_HTMLTAG },
1413                 { "ispredefined", FT_PREDEFINED },
1414                 { "listcommand", FT_LISTCOMMAND },
1415                 { "listname", FT_LISTNAME },
1416                 { "numberwithin", FT_WITHIN },
1417                 { "placement", FT_PLACEMENT },
1418                 { "refprefix", FT_REFPREFIX },
1419                 { "requires", FT_REQUIRES },
1420                 { "style", FT_STYLE },
1421                 { "type", FT_TYPE },
1422                 { "usesfloatpkg", FT_USESFLOAT }
1423         };
1424
1425         lexrc.pushTable(floatTags);
1426
1427         string ext;
1428         string htmlattr;
1429         docstring htmlstyle;
1430         string htmltag;
1431         string listname;
1432         string listcommand;
1433         string name;
1434         string placement;
1435         string allowed_placement = "!htbpH";
1436         string refprefix;
1437         string style;
1438         string type;
1439         string within;
1440         string requires;
1441         bool usesfloat = true;
1442         bool ispredefined = false;
1443         bool allowswide = true;
1444         bool allowssideways = true;
1445
1446         bool getout = false;
1447         while (!getout && lexrc.isOK()) {
1448                 int le = lexrc.lex();
1449                 switch (le) {
1450                 case Lexer::LEX_UNDEF:
1451                         lexrc.printError("Unknown float tag `$$Token'");
1452                         continue;
1453                 default:
1454                         break;
1455                 }
1456                 switch (le) {
1457                 case FT_TYPE:
1458                         lexrc.next();
1459                         type = lexrc.getString();
1460                         if (floatlist_.typeExist(type)) {
1461                                 Floating const & fl = floatlist_.getType(type);
1462                                 placement = fl.placement();
1463                                 ext = fl.ext();
1464                                 within = fl.within();
1465                                 style = fl.style();
1466                                 name = fl.name();
1467                                 listname = fl.listName();
1468                                 usesfloat = fl.usesFloatPkg();
1469                                 ispredefined = fl.isPredefined();
1470                                 listcommand = fl.listCommand();
1471                                 refprefix = fl.refPrefix();
1472                         }
1473                         break;
1474                 case FT_NAME:
1475                         lexrc.next();
1476                         name = lexrc.getString();
1477                         break;
1478                 case FT_PLACEMENT:
1479                         lexrc.next();
1480                         placement = lexrc.getString();
1481                         break;
1482                 case FT_ALLOWED_PLACEMENT:
1483                         lexrc.next();
1484                         allowed_placement = lexrc.getString();
1485                         break;
1486                 case FT_EXT:
1487                         lexrc.next();
1488                         ext = lexrc.getString();
1489                         break;
1490                 case FT_WITHIN:
1491                         lexrc.next();
1492                         within = lexrc.getString();
1493                         if (within == "none")
1494                                 within.erase();
1495                         break;
1496                 case FT_STYLE:
1497                         lexrc.next();
1498                         style = lexrc.getString();
1499                         break;
1500                 case FT_LISTCOMMAND:
1501                         lexrc.next();
1502                         listcommand = lexrc.getString();
1503                         break;
1504                 case FT_REFPREFIX:
1505                         lexrc.next();
1506                         refprefix = lexrc.getString();
1507                         break;
1508                 case FT_LISTNAME:
1509                         lexrc.next();
1510                         listname = lexrc.getString();
1511                         break;
1512                 case FT_USESFLOAT:
1513                         lexrc.next();
1514                         usesfloat = lexrc.getBool();
1515                         break;
1516                 case FT_REQUIRES:
1517                         lexrc.next();
1518                         requires = lexrc.getString();
1519                         break;
1520                 case FT_PREDEFINED:
1521                         lexrc.next();
1522                         ispredefined = lexrc.getBool();
1523                         break;
1524                 case FT_ALLOWS_SIDEWAYS:
1525                         lexrc.next();
1526                         allowssideways = lexrc.getBool();
1527                         break;
1528                 case FT_ALLOWS_WIDE:
1529                         lexrc.next();
1530                         allowswide = lexrc.getBool();
1531                         break;
1532                 case FT_HTMLATTR:
1533                         lexrc.next();
1534                         htmlattr = lexrc.getString();
1535                         break;
1536                 case FT_HTMLSTYLE:
1537                         lexrc.next();
1538                         htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1539                         break;
1540                 case FT_HTMLTAG:
1541                         lexrc.next();
1542                         htmltag = lexrc.getString();
1543                         break;
1544                 case FT_END:
1545                         getout = true;
1546                         break;
1547                 }
1548         }
1549
1550         lexrc.popTable();
1551
1552         // Here we have a full float if getout == true
1553         if (getout) {
1554                 if (!usesfloat && listcommand.empty()) {
1555                         // if this float uses the same auxfile as an existing one,
1556                         // there is no need for it to provide a list command.
1557                         FloatList::const_iterator it = floatlist_.begin();
1558                         FloatList::const_iterator en = floatlist_.end();
1559                         bool found_ext = false;
1560                         for (; it != en; ++it) {
1561                                 if (it->second.ext() == ext) {
1562                                         found_ext = true;
1563                                         break;
1564                                 }
1565                         }
1566                         if (!found_ext)
1567                                 LYXERR0("The layout does not provide a list command " <<
1568                                   "for the float `" << type << "'. LyX will " <<
1569                                   "not be able to produce a float list.");
1570                 }
1571                 Floating fl(type, placement, ext, within, style, name,
1572                             listname, listcommand, refprefix, allowed_placement,
1573                             htmltag, htmlattr, htmlstyle, requires, usesfloat,
1574                             ispredefined, allowswide, allowssideways);
1575                 floatlist_.newFloat(fl);
1576                 // each float has its own counter
1577                 counters_.newCounter(from_ascii(type), from_ascii(within),
1578                                       docstring(), docstring());
1579                 // also define sub-float counters
1580                 docstring const subtype = "sub-" + from_ascii(type);
1581                 counters_.newCounter(subtype, from_ascii(type),
1582                                       "\\alph{" + subtype + "}", docstring());
1583         }
1584         return getout;
1585 }
1586
1587
1588 bool TextClass::readOutlinerName(Lexer & lexrc)
1589 {
1590         std::string type;
1591         docstring name;
1592         if (lexrc.next())
1593                 type = lexrc.getString();
1594         else {
1595                 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1596                 return false;
1597         }
1598         if (lexrc.next())
1599                 name = lexrc.getDocString();
1600         else {
1601                 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1602                 return false;
1603         }
1604         outliner_names_[type] = name;
1605     return true;
1606 }
1607
1608
1609 string const & TextClass::prerequisites(string const & sep) const
1610 {
1611         if (contains(prerequisites_, ',')) {
1612                 vector<string> const pres = getVectorFromString(prerequisites_);
1613                 prerequisites_ = getStringFromVector(pres, sep);
1614         }
1615         return prerequisites_;
1616 }
1617
1618
1619 bool TextClass::hasLayout(docstring const & n) const
1620 {
1621         docstring const name = n.empty() ? defaultLayoutName() : n;
1622
1623         return find_if(layoutlist_.begin(), layoutlist_.end(),
1624                        LayoutNamesEqual(name))
1625                 != layoutlist_.end();
1626 }
1627
1628
1629 bool TextClass::hasInsetLayout(docstring const & n) const
1630 {
1631         if (n.empty())
1632                 return false;
1633         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1634         return it != insetlayoutlist_.end();
1635 }
1636
1637
1638 Layout const & TextClass::operator[](docstring const & name) const
1639 {
1640         LATTEST(!name.empty());
1641
1642         const_iterator it =
1643                 find_if(begin(), end(), LayoutNamesEqual(name));
1644
1645         if (it == end()) {
1646                 LYXERR0("We failed to find the layout '" << name
1647                        << "' in the layout list. You MUST investigate!");
1648                 for (const_iterator cit = begin(); cit != end(); ++cit)
1649                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1650
1651                 // We require the name to exist
1652                 static const Layout dummy;
1653                 LASSERT(false, return dummy);
1654         }
1655
1656         return *it;
1657 }
1658
1659
1660 Layout & TextClass::operator[](docstring const & name)
1661 {
1662         LATTEST(!name.empty());
1663         // Safe to continue, given what we do below.
1664
1665         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1666
1667         if (it == end()) {
1668                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1669                        << "' in the layout list. You MUST investigate!");
1670                 for (const_iterator cit = begin(); cit != end(); ++cit)
1671                         LYXERR0(" " << to_utf8(cit->name()));
1672
1673                 // we require the name to exist
1674                 LATTEST(false);
1675                 // we are here only in release mode
1676                 layoutlist_.push_back(createBasicLayout(name, true));
1677                 it = find_if(begin(), end(), LayoutNamesEqual(name));
1678         }
1679
1680         return *it;
1681 }
1682
1683
1684 bool TextClass::deleteLayout(docstring const & name)
1685 {
1686         if (name == defaultLayoutName() || name == plainLayoutName())
1687                 return false;
1688
1689         LayoutList::iterator it =
1690                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1691                           LayoutNamesEqual(name));
1692
1693         LayoutList::iterator end = layoutlist_.end();
1694         bool const ret = (it != end);
1695         layoutlist_.erase(it, end);
1696         return ret;
1697 }
1698
1699
1700 bool TextClass::deleteInsetLayout(docstring const & name)
1701 {
1702         return insetlayoutlist_.erase(name);
1703 }
1704
1705
1706 // Load textclass info if not loaded yet
1707 bool TextClass::load(string const & path) const
1708 {
1709         if (loaded_)
1710                 return true;
1711
1712         // Read style-file, provided path is searched before the system ones
1713         // If path is a file, it is loaded directly.
1714         FileName layout_file(path);
1715         if (!path.empty() && !layout_file.isReadableFile())
1716                 layout_file = FileName(addName(path, name_ + ".layout"));
1717         if (layout_file.empty() || !layout_file.exists())
1718                 layout_file = libFileSearch("layouts", name_, "layout");
1719         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1720
1721         if (!loaded_) {
1722                 lyxerr << "Error reading `"
1723                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1724                        << "'\n(Check `" << name_
1725                        << "')\nCheck your installation and "
1726                           "try Options/Reconfigure..."
1727                        << endl;
1728         }
1729
1730         return loaded_;
1731 }
1732
1733
1734 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1735 {
1736         if (hasLayout(n))
1737                 return false;
1738
1739         layoutlist_.push_back(createBasicLayout(n, true));
1740         return true;
1741 }
1742
1743
1744 string DocumentClass::forcedLayouts() const
1745 {
1746         ostringstream os;
1747         bool first = true;
1748         const_iterator const e = end();
1749         for (const_iterator i = begin(); i != e; ++i) {
1750                 if (i->forcelocal > 0) {
1751                         if (first) {
1752                                 os << "Format " << LAYOUT_FORMAT << '\n';
1753                                 first = false;
1754                         }
1755                         i->write(os);
1756                 }
1757         }
1758         return os.str();
1759 }
1760
1761
1762 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1763 {
1764         // FIXME The fix for the InsetLayout part of 4812 would be here:
1765         // Add the InsetLayout to the document class if it is not found.
1766         docstring n = name;
1767         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1768         while (!n.empty()) {
1769                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1770                 if (cit != cen && cit->first == n) {
1771                         if (cit->second.obsoleted_by().empty())
1772                                 return cit->second;
1773                         n = cit->second.obsoleted_by();
1774                         return insetLayout(n);
1775                 }
1776                 // If we have a generic prefix (e.g., "Note:"),
1777                 // try if this one alone is found.
1778                 size_t i = n.find(':');
1779                 if (i == string::npos)
1780                         break;
1781                 n = n.substr(0, i);
1782         }
1783         // Layout "name" not found.
1784         return plainInsetLayout();
1785 }
1786
1787
1788 InsetLayout const & DocumentClass::plainInsetLayout() {
1789         static const InsetLayout plain_insetlayout_;
1790         return plain_insetlayout_;
1791 }
1792
1793
1794 docstring const & TextClass::defaultLayoutName() const
1795 {
1796         return defaultlayout_;
1797 }
1798
1799
1800 Layout const & TextClass::defaultLayout() const
1801 {
1802         return operator[](defaultLayoutName());
1803 }
1804
1805
1806 bool TextClass::isDefaultLayout(Layout const & layout) const
1807 {
1808         return layout.name() == defaultLayoutName();
1809 }
1810
1811
1812 bool TextClass::isPlainLayout(Layout const & layout) const
1813 {
1814         return layout.name() == plainLayoutName();
1815 }
1816
1817
1818 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1819 {
1820         static Layout * defaultLayout = NULL;
1821
1822         if (defaultLayout) {
1823                 defaultLayout->setUnknown(unknown);
1824                 defaultLayout->setName(name);
1825                 return *defaultLayout;
1826         }
1827
1828         static char const * s = "Margin Static\n"
1829                         "LatexType Paragraph\n"
1830                         "LatexName dummy\n"
1831                         "Align Block\n"
1832                         "AlignPossible Left, Right, Center\n"
1833                         "LabelType No_Label\n"
1834                         "End";
1835         istringstream ss(s);
1836         Lexer lex(textClassTags);
1837         lex.setStream(ss);
1838         defaultLayout = new Layout;
1839         defaultLayout->setUnknown(unknown);
1840         defaultLayout->setName(name);
1841         if (!readStyle(lex, *defaultLayout)) {
1842                 // The only way this happens is because the hardcoded layout above
1843                 // is wrong.
1844                 LATTEST(false);
1845         };
1846         return *defaultLayout;
1847 }
1848
1849
1850 DocumentClassPtr getDocumentClass(
1851                 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1852                 string const & cengine, bool const clone)
1853 {
1854         DocumentClassPtr doc_class =
1855             DocumentClassPtr(new DocumentClass(baseClass));
1856         LayoutModuleList::const_iterator it = modlist.begin();
1857         LayoutModuleList::const_iterator en = modlist.end();
1858         for (; it != en; ++it) {
1859                 string const modName = *it;
1860                 LyXModule * lm = theModuleList[modName];
1861                 if (!lm) {
1862                         docstring const msg =
1863                                                 bformat(_("The module %1$s has been requested by\n"
1864                                                 "this document but has not been found in the list of\n"
1865                                                 "available modules. If you recently installed it, you\n"
1866                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1867                         if (!clone)
1868                                 frontend::Alert::warning(_("Module not available"), msg);
1869                         continue;
1870                 }
1871                 if (!lm->isAvailable() && !clone) {
1872                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1873                         docstring const msg =
1874                                 bformat(_("The module %1$s requires a package that is not\n"
1875                                         "available in your LaTeX installation, or a converter that\n"
1876                                         "you have not installed. LaTeX output may not be possible.\n"
1877                                         "Missing prerequisites:\n"
1878                                                 "\t%2$s\n"
1879                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1880                                 from_utf8(modName), prereqs);
1881                         frontend::Alert::warning(_("Package not available"), msg, true);
1882                 }
1883                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1884                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1885                         docstring const msg =
1886                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1887                         frontend::Alert::warning(_("Read Error"), msg);
1888                 }
1889         }
1890
1891         if (cengine.empty())
1892                 return doc_class;
1893
1894         LyXCiteEngine * ce = theCiteEnginesList[cengine];
1895         if (!ce) {
1896                 docstring const msg =
1897                                         bformat(_("The cite engine %1$s has been requested by\n"
1898                                         "this document but has not been found in the list of\n"
1899                                         "available engines. If you recently installed it, you\n"
1900                                         "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1901                 if (!clone)
1902                         frontend::Alert::warning(_("Cite Engine not available"), msg);
1903         } else if (!ce->isAvailable() && !clone) {
1904                 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1905                 docstring const msg =
1906                         bformat(_("The cite engine %1$s requires a package that is not\n"
1907                                 "available in your LaTeX installation, or a converter that\n"
1908                                 "you have not installed. LaTeX output may not be possible.\n"
1909                                 "Missing prerequisites:\n"
1910                                         "\t%2$s\n"
1911                                 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1912                         from_utf8(cengine), prereqs);
1913                 frontend::Alert::warning(_("Package not available"), msg, true);
1914         } else {
1915                 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1916                 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1917                         docstring const msg =
1918                                                 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1919                         frontend::Alert::warning(_("Read Error"), msg);
1920                 }
1921         }
1922
1923         return doc_class;
1924 }
1925
1926
1927 /////////////////////////////////////////////////////////////////////////
1928 //
1929 // DocumentClass
1930 //
1931 /////////////////////////////////////////////////////////////////////////
1932
1933 DocumentClass::DocumentClass(LayoutFile const & tc)
1934         : TextClass(tc)
1935 {}
1936
1937
1938 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1939 {
1940         LayoutList::const_iterator it  = layoutlist_.begin();
1941         LayoutList::const_iterator end = layoutlist_.end();
1942         for (; it != end; ++it)
1943                 if (it->latexname() == lay)
1944                         return true;
1945         return false;
1946 }
1947
1948
1949 bool DocumentClass::provides(string const & p) const
1950 {
1951         return provides_.find(p) != provides_.end();
1952 }
1953
1954
1955 bool DocumentClass::hasTocLevels() const
1956 {
1957         return min_toclevel_ != Layout::NOT_IN_TOC;
1958 }
1959
1960
1961 Layout const & DocumentClass::getTOCLayout() const
1962 {
1963         // we're going to look for the layout with the minimum toclevel
1964         TextClass::LayoutList::const_iterator lit = begin();
1965         TextClass::LayoutList::const_iterator const len = end();
1966         int minlevel = 1000;
1967         Layout const * lay = NULL;
1968         for (; lit != len; ++lit) {
1969                 int const level = lit->toclevel;
1970                 // we don't want Part or unnumbered sections
1971                 if (level == Layout::NOT_IN_TOC || level < 0
1972                     || level >= minlevel || lit->counter.empty())
1973                         continue;
1974                 lay = &*lit;
1975                 minlevel = level;
1976         }
1977         if (lay)
1978                 return *lay;
1979         // hmm. that is very odd, so we'll do our best.
1980         return operator[](defaultLayoutName());
1981 }
1982
1983
1984 Layout const & DocumentClass::htmlTOCLayout() const
1985 {
1986         if (html_toc_section_.empty())
1987                 html_toc_section_ = getTOCLayout().name();
1988         return operator[](html_toc_section_);
1989 }
1990
1991
1992 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1993         string const & entry, bool const punct, string const & fallback) const
1994 {
1995         string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
1996                                 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
1997                                 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
1998         if (punct)
1999                 default_format += ".";
2000
2001         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
2002         if (itype == cite_formats_.end())
2003                 return default_format;
2004         map<string, string>::const_iterator it = itype->second.find(entry);
2005         if (it == itype->second.end() && !fallback.empty())
2006                 it = itype->second.find(fallback);
2007         if (it == itype->second.end())
2008                 return default_format;
2009         if (punct)
2010                 return it->second + ".";
2011         return it->second;
2012 }
2013
2014
2015 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2016         string const & macro) const
2017 {
2018         static string empty;
2019         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2020         if (itype == cite_macros_.end())
2021                 return empty;
2022         map<string, string>::const_iterator it = itype->second.find(macro);
2023         if (it == itype->second.end())
2024                 return empty;
2025         return it->second;
2026 }
2027
2028
2029 vector<string> const DocumentClass::citeCommands(
2030         CiteEngineType const & type) const
2031 {
2032         vector<CitationStyle> const styles = citeStyles(type);
2033         vector<CitationStyle>::const_iterator it = styles.begin();
2034         vector<CitationStyle>::const_iterator end = styles.end();
2035         vector<string> cmds;
2036         for (; it != end; ++it) {
2037                 CitationStyle const cite = *it;
2038                 cmds.push_back(cite.name);
2039         }
2040         return cmds;
2041 }
2042
2043
2044 vector<CitationStyle> const & DocumentClass::citeStyles(
2045         CiteEngineType const & type) const
2046 {
2047         static vector<CitationStyle> empty;
2048         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2049         if (it == cite_styles_.end())
2050                 return empty;
2051         return it->second;
2052 }
2053
2054
2055 /////////////////////////////////////////////////////////////////////////
2056 //
2057 // PageSides
2058 //
2059 /////////////////////////////////////////////////////////////////////////
2060
2061 ostream & operator<<(ostream & os, PageSides p)
2062 {
2063         switch (p) {
2064         case OneSide:
2065                 os << '1';
2066                 break;
2067         case TwoSides:
2068                 os << '2';
2069                 break;
2070         }
2071         return os;
2072 }
2073
2074
2075 } // namespace lyx