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