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