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