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