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