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