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