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