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