]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
2fb1bd28a2139b1986ed3519a648141682aa4cb5
[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                         // Check if the macro is already def'ed
1284                         for (auto const & cm : cite_macros_) {
1285                                 if (!(type & cm.first))
1286                                         continue;
1287                                 if (cm.second.find(etype) != cm.second.end())
1288                                         defined = true;
1289                         }
1290                         if (!defined || overwrite) {
1291                                 if (type & ENGINE_TYPE_AUTHORYEAR)
1292                                         cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1293                                 if (type & ENGINE_TYPE_NUMERICAL)
1294                                         cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1295                                 if (type & ENGINE_TYPE_DEFAULT)
1296                                         cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1297                         }
1298                 } else {
1299                         bool defined = false;
1300                         // Check if the format is already def'ed
1301                         for (auto const & cm : cite_formats_) {
1302                                 if (!(type & cm.first))
1303                                         continue;
1304                                 if (cm.second.find(etype) != cm.second.end())
1305                                         defined = true;
1306                         }
1307                         if (!defined || overwrite){
1308                                 if (type & ENGINE_TYPE_AUTHORYEAR)
1309                                         cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1310                                 if (type & ENGINE_TYPE_NUMERICAL)
1311                                         cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1312                                 if (type & ENGINE_TYPE_DEFAULT)
1313                                         cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1314                         }
1315                 }
1316         }
1317         return true;
1318 }
1319
1320
1321 bool TextClass::readFloat(Lexer & lexrc)
1322 {
1323         enum {
1324                 FT_TYPE = 1,
1325                 FT_NAME,
1326                 FT_PLACEMENT,
1327                 FT_EXT,
1328                 FT_WITHIN,
1329                 FT_STYLE,
1330                 FT_LISTNAME,
1331                 FT_USESFLOAT,
1332                 FT_PREDEFINED,
1333                 FT_HTMLSTYLE,
1334                 FT_HTMLATTR,
1335                 FT_HTMLTAG,
1336                 FT_LISTCOMMAND,
1337                 FT_REFPREFIX,
1338                 FT_ALLOWED_PLACEMENT,
1339                 FT_ALLOWS_SIDEWAYS,
1340                 FT_ALLOWS_WIDE,
1341                 FT_END
1342         };
1343
1344         LexerKeyword floatTags[] = {
1345                 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1346                 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1347                 { "allowswide", FT_ALLOWS_WIDE },
1348                 { "end", FT_END },
1349                 { "extension", FT_EXT },
1350                 { "guiname", FT_NAME },
1351                 { "htmlattr", FT_HTMLATTR },
1352                 { "htmlstyle", FT_HTMLSTYLE },
1353                 { "htmltag", FT_HTMLTAG },
1354                 { "ispredefined", FT_PREDEFINED },
1355                 { "listcommand", FT_LISTCOMMAND },
1356                 { "listname", FT_LISTNAME },
1357                 { "numberwithin", FT_WITHIN },
1358                 { "placement", FT_PLACEMENT },
1359                 { "refprefix", FT_REFPREFIX },
1360                 { "style", FT_STYLE },
1361                 { "type", FT_TYPE },
1362                 { "usesfloatpkg", FT_USESFLOAT }
1363         };
1364
1365         lexrc.pushTable(floatTags);
1366
1367         string ext;
1368         string htmlattr;
1369         docstring htmlstyle;
1370         string htmltag;
1371         string listname;
1372         string listcommand;
1373         string name;
1374         string placement;
1375         string allowed_placement = "!htbpH";
1376         string refprefix;
1377         string style;
1378         string type;
1379         string within;
1380         bool usesfloat = true;
1381         bool ispredefined = false;
1382         bool allowswide = true;
1383         bool allowssideways = true;
1384
1385         bool getout = false;
1386         while (!getout && lexrc.isOK()) {
1387                 int le = lexrc.lex();
1388                 switch (le) {
1389                 case Lexer::LEX_UNDEF:
1390                         lexrc.printError("Unknown float tag `$$Token'");
1391                         continue;
1392                 default:
1393                         break;
1394                 }
1395                 switch (le) {
1396                 case FT_TYPE:
1397                         lexrc.next();
1398                         type = lexrc.getString();
1399                         if (floatlist_.typeExist(type)) {
1400                                 Floating const & fl = floatlist_.getType(type);
1401                                 placement = fl.placement();
1402                                 ext = fl.ext();
1403                                 within = fl.within();
1404                                 style = fl.style();
1405                                 name = fl.name();
1406                                 listname = fl.listName();
1407                                 usesfloat = fl.usesFloatPkg();
1408                                 ispredefined = fl.isPredefined();
1409                                 listcommand = fl.listCommand();
1410                                 refprefix = fl.refPrefix();
1411                         }
1412                         break;
1413                 case FT_NAME:
1414                         lexrc.next();
1415                         name = lexrc.getString();
1416                         break;
1417                 case FT_PLACEMENT:
1418                         lexrc.next();
1419                         placement = lexrc.getString();
1420                         break;
1421                 case FT_ALLOWED_PLACEMENT:
1422                         lexrc.next();
1423                         allowed_placement = lexrc.getString();
1424                         break;
1425                 case FT_EXT:
1426                         lexrc.next();
1427                         ext = lexrc.getString();
1428                         break;
1429                 case FT_WITHIN:
1430                         lexrc.next();
1431                         within = lexrc.getString();
1432                         if (within == "none")
1433                                 within.erase();
1434                         break;
1435                 case FT_STYLE:
1436                         lexrc.next();
1437                         style = lexrc.getString();
1438                         break;
1439                 case FT_LISTCOMMAND:
1440                         lexrc.next();
1441                         listcommand = lexrc.getString();
1442                         break;
1443                 case FT_REFPREFIX:
1444                         lexrc.next();
1445                         refprefix = lexrc.getString();
1446                         break;
1447                 case FT_LISTNAME:
1448                         lexrc.next();
1449                         listname = lexrc.getString();
1450                         break;
1451                 case FT_USESFLOAT:
1452                         lexrc.next();
1453                         usesfloat = lexrc.getBool();
1454                         break;
1455                 case FT_PREDEFINED:
1456                         lexrc.next();
1457                         ispredefined = lexrc.getBool();
1458                         break;
1459                 case FT_ALLOWS_SIDEWAYS:
1460                         lexrc.next();
1461                         allowssideways = lexrc.getBool();
1462                         break;
1463                 case FT_ALLOWS_WIDE:
1464                         lexrc.next();
1465                         allowswide = lexrc.getBool();
1466                         break;
1467                 case FT_HTMLATTR:
1468                         lexrc.next();
1469                         htmlattr = lexrc.getString();
1470                         break;
1471                 case FT_HTMLSTYLE:
1472                         lexrc.next();
1473                         htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1474                         break;
1475                 case FT_HTMLTAG:
1476                         lexrc.next();
1477                         htmltag = lexrc.getString();
1478                         break;
1479                 case FT_END:
1480                         getout = true;
1481                         break;
1482                 }
1483         }
1484
1485         lexrc.popTable();
1486
1487         // Here we have a full float if getout == true
1488         if (getout) {
1489                 if (!usesfloat && listcommand.empty()) {
1490                         // if this float uses the same auxfile as an existing one,
1491                         // there is no need for it to provide a list command.
1492                         FloatList::const_iterator it = floatlist_.begin();
1493                         FloatList::const_iterator en = floatlist_.end();
1494                         bool found_ext = false;
1495                         for (; it != en; ++it) {
1496                                 if (it->second.ext() == ext) {
1497                                         found_ext = true;
1498                                         break;
1499                                 }
1500                         }
1501                         if (!found_ext)
1502                                 LYXERR0("The layout does not provide a list command " <<
1503                                   "for the float `" << type << "'. LyX will " <<
1504                                   "not be able to produce a float list.");
1505                 }
1506                 Floating fl(type, placement, ext, within, style, name,
1507                             listname, listcommand, refprefix, allowed_placement,
1508                             htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1509                             allowswide, allowssideways);
1510                 floatlist_.newFloat(fl);
1511                 // each float has its own counter
1512                 counters_.newCounter(from_ascii(type), from_ascii(within),
1513                                       docstring(), docstring());
1514                 // also define sub-float counters
1515                 docstring const subtype = "sub-" + from_ascii(type);
1516                 counters_.newCounter(subtype, from_ascii(type),
1517                                       "\\alph{" + subtype + "}", docstring());
1518         }
1519         return getout;
1520 }
1521
1522
1523 bool TextClass::readOutlinerName(Lexer & lexrc)
1524 {
1525         std::string type;
1526         docstring name;
1527         if (lexrc.next())
1528                 type = lexrc.getString();
1529         else {
1530                 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1531                 return false;
1532         }
1533         if (lexrc.next())
1534                 name = lexrc.getDocString();
1535         else {
1536                 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1537                 return false;
1538         }
1539         outliner_names_[type] = name;
1540     return true;
1541 }
1542
1543
1544 string const & TextClass::prerequisites(string const & sep) const
1545 {
1546         if (contains(prerequisites_, ',')) {
1547                 vector<string> const pres = getVectorFromString(prerequisites_);
1548                 prerequisites_ = getStringFromVector(pres, sep);
1549         }
1550         return prerequisites_;
1551 }
1552
1553
1554 bool TextClass::hasLayout(docstring const & n) const
1555 {
1556         docstring const name = n.empty() ? defaultLayoutName() : n;
1557
1558         return find_if(layoutlist_.begin(), layoutlist_.end(),
1559                        LayoutNamesEqual(name))
1560                 != layoutlist_.end();
1561 }
1562
1563
1564 bool TextClass::hasInsetLayout(docstring const & n) const
1565 {
1566         if (n.empty())
1567                 return false;
1568         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1569         return it != insetlayoutlist_.end();
1570 }
1571
1572
1573 Layout const & TextClass::operator[](docstring const & name) const
1574 {
1575         LATTEST(!name.empty());
1576
1577         const_iterator it =
1578                 find_if(begin(), end(), LayoutNamesEqual(name));
1579
1580         if (it == end()) {
1581                 LYXERR0("We failed to find the layout '" << name
1582                        << "' in the layout list. You MUST investigate!");
1583                 for (const_iterator cit = begin(); cit != end(); ++cit)
1584                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1585
1586                 // We require the name to exist
1587                 static const Layout dummy;
1588                 LASSERT(false, return dummy);
1589         }
1590
1591         return *it;
1592 }
1593
1594
1595 Layout & TextClass::operator[](docstring const & name)
1596 {
1597         LATTEST(!name.empty());
1598         // Safe to continue, given what we do below.
1599
1600         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1601
1602         if (it == end()) {
1603                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1604                        << "' in the layout list. You MUST investigate!");
1605                 for (const_iterator cit = begin(); cit != end(); ++cit)
1606                         LYXERR0(" " << to_utf8(cit->name()));
1607
1608                 // we require the name to exist
1609                 LATTEST(false);
1610                 // we are here only in release mode
1611                 layoutlist_.push_back(createBasicLayout(name, true));
1612                 it = find_if(begin(), end(), LayoutNamesEqual(name));
1613         }
1614
1615         return *it;
1616 }
1617
1618
1619 bool TextClass::deleteLayout(docstring const & name)
1620 {
1621         if (name == defaultLayoutName() || name == plainLayoutName())
1622                 return false;
1623
1624         LayoutList::iterator it =
1625                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1626                           LayoutNamesEqual(name));
1627
1628         LayoutList::iterator end = layoutlist_.end();
1629         bool const ret = (it != end);
1630         layoutlist_.erase(it, end);
1631         return ret;
1632 }
1633
1634
1635 bool TextClass::deleteInsetLayout(docstring const & name)
1636 {
1637         return insetlayoutlist_.erase(name);
1638 }
1639
1640
1641 // Load textclass info if not loaded yet
1642 bool TextClass::load(string const & path) const
1643 {
1644         if (loaded_)
1645                 return true;
1646
1647         // Read style-file, provided path is searched before the system ones
1648         // If path is a file, it is loaded directly.
1649         FileName layout_file(path);
1650         if (!path.empty() && !layout_file.isReadableFile())
1651                 layout_file = FileName(addName(path, name_ + ".layout"));
1652         if (layout_file.empty() || !layout_file.exists())
1653                 layout_file = libFileSearch("layouts", name_, "layout");
1654         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1655
1656         if (!loaded_) {
1657                 lyxerr << "Error reading `"
1658                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1659                        << "'\n(Check `" << name_
1660                        << "')\nCheck your installation and "
1661                           "try Options/Reconfigure..."
1662                        << endl;
1663         }
1664
1665         return loaded_;
1666 }
1667
1668
1669 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1670 {
1671         if (hasLayout(n))
1672                 return false;
1673
1674         layoutlist_.push_back(createBasicLayout(n, true));
1675         return true;
1676 }
1677
1678
1679 string DocumentClass::forcedLayouts() const
1680 {
1681         ostringstream os;
1682         bool first = true;
1683         const_iterator const e = end();
1684         for (const_iterator i = begin(); i != e; ++i) {
1685                 if (i->forcelocal > 0) {
1686                         if (first) {
1687                                 os << "Format " << LAYOUT_FORMAT << '\n';
1688                                 first = false;
1689                         }
1690                         i->write(os);
1691                 }
1692         }
1693         return os.str();
1694 }
1695
1696
1697 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1698 {
1699         // FIXME The fix for the InsetLayout part of 4812 would be here:
1700         // Add the InsetLayout to the document class if it is not found.
1701         docstring n = name;
1702         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1703         while (!n.empty()) {
1704                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1705                 if (cit != cen && cit->first == n) {
1706                         if (cit->second.obsoleted_by().empty())
1707                                 return cit->second;
1708                         n = cit->second.obsoleted_by();
1709                         return insetLayout(n);
1710                 }
1711                 // If we have a generic prefix (e.g., "Note:"),
1712                 // try if this one alone is found.
1713                 size_t i = n.find(':');
1714                 if (i == string::npos)
1715                         break;
1716                 n = n.substr(0, i);
1717         }
1718         // Layout "name" not found.
1719         return plainInsetLayout();
1720 }
1721
1722
1723 InsetLayout const & DocumentClass::plainInsetLayout() {
1724         static const InsetLayout plain_insetlayout_;
1725         return plain_insetlayout_;
1726 }
1727
1728
1729 docstring const & TextClass::defaultLayoutName() const
1730 {
1731         return defaultlayout_;
1732 }
1733
1734
1735 Layout const & TextClass::defaultLayout() const
1736 {
1737         return operator[](defaultLayoutName());
1738 }
1739
1740
1741 bool TextClass::isDefaultLayout(Layout const & layout) const
1742 {
1743         return layout.name() == defaultLayoutName();
1744 }
1745
1746
1747 bool TextClass::isPlainLayout(Layout const & layout) const
1748 {
1749         return layout.name() == plainLayoutName();
1750 }
1751
1752
1753 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1754 {
1755         static Layout * defaultLayout = NULL;
1756
1757         if (defaultLayout) {
1758                 defaultLayout->setUnknown(unknown);
1759                 defaultLayout->setName(name);
1760                 return *defaultLayout;
1761         }
1762
1763         static char const * s = "Margin Static\n"
1764                         "LatexType Paragraph\n"
1765                         "LatexName dummy\n"
1766                         "Align Block\n"
1767                         "AlignPossible Left, Right, Center\n"
1768                         "LabelType No_Label\n"
1769                         "End";
1770         istringstream ss(s);
1771         Lexer lex(textClassTags);
1772         lex.setStream(ss);
1773         defaultLayout = new Layout;
1774         defaultLayout->setUnknown(unknown);
1775         defaultLayout->setName(name);
1776         if (!readStyle(lex, *defaultLayout)) {
1777                 // The only way this happens is because the hardcoded layout above
1778                 // is wrong.
1779                 LATTEST(false);
1780         };
1781         return *defaultLayout;
1782 }
1783
1784
1785 DocumentClassPtr getDocumentClass(
1786                 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1787                 string const & cengine, bool const clone)
1788 {
1789         DocumentClassPtr doc_class =
1790             DocumentClassPtr(new DocumentClass(baseClass));
1791         LayoutModuleList::const_iterator it = modlist.begin();
1792         LayoutModuleList::const_iterator en = modlist.end();
1793         for (; it != en; ++it) {
1794                 string const modName = *it;
1795                 LyXModule * lm = theModuleList[modName];
1796                 if (!lm) {
1797                         docstring const msg =
1798                                                 bformat(_("The module %1$s has been requested by\n"
1799                                                 "this document but has not been found in the list of\n"
1800                                                 "available modules. If you recently installed it, you\n"
1801                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1802                         if (!clone)
1803                                 frontend::Alert::warning(_("Module not available"), msg);
1804                         continue;
1805                 }
1806                 if (!lm->isAvailable() && !clone) {
1807                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1808                         docstring const msg =
1809                                 bformat(_("The module %1$s requires a package that is not\n"
1810                                         "available in your LaTeX installation, or a converter that\n"
1811                                         "you have not installed. LaTeX output may not be possible.\n"
1812                                         "Missing prerequisites:\n"
1813                                                 "\t%2$s\n"
1814                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1815                                 from_utf8(modName), prereqs);
1816                         frontend::Alert::warning(_("Package not available"), msg, true);
1817                 }
1818                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1819                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1820                         docstring const msg =
1821                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1822                         frontend::Alert::warning(_("Read Error"), msg);
1823                 }
1824         }
1825
1826         if (cengine.empty())
1827                 return doc_class;
1828
1829         LyXCiteEngine * ce = theCiteEnginesList[cengine];
1830         if (!ce) {
1831                 docstring const msg =
1832                                         bformat(_("The cite engine %1$s has been requested by\n"
1833                                         "this document but has not been found in the list of\n"
1834                                         "available engines. If you recently installed it, you\n"
1835                                         "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1836                 if (!clone)
1837                         frontend::Alert::warning(_("Cite Engine not available"), msg);
1838         } else if (!ce->isAvailable() && !clone) {
1839                 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1840                 docstring const msg =
1841                         bformat(_("The cite engine %1$s requires a package that is not\n"
1842                                 "available in your LaTeX installation, or a converter that\n"
1843                                 "you have not installed. LaTeX output may not be possible.\n"
1844                                 "Missing prerequisites:\n"
1845                                         "\t%2$s\n"
1846                                 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1847                         from_utf8(cengine), prereqs);
1848                 frontend::Alert::warning(_("Package not available"), msg, true);
1849         } else {
1850                 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1851                 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1852                         docstring const msg =
1853                                                 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1854                         frontend::Alert::warning(_("Read Error"), msg);
1855                 }
1856         }
1857
1858         return doc_class;
1859 }
1860
1861
1862 /////////////////////////////////////////////////////////////////////////
1863 //
1864 // DocumentClass
1865 //
1866 /////////////////////////////////////////////////////////////////////////
1867
1868 DocumentClass::DocumentClass(LayoutFile const & tc)
1869         : TextClass(tc)
1870 {}
1871
1872
1873 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1874 {
1875         LayoutList::const_iterator it  = layoutlist_.begin();
1876         LayoutList::const_iterator end = layoutlist_.end();
1877         for (; it != end; ++it)
1878                 if (it->latexname() == lay)
1879                         return true;
1880         return false;
1881 }
1882
1883
1884 bool DocumentClass::provides(string const & p) const
1885 {
1886         return provides_.find(p) != provides_.end();
1887 }
1888
1889
1890 bool DocumentClass::hasTocLevels() const
1891 {
1892         return min_toclevel_ != Layout::NOT_IN_TOC;
1893 }
1894
1895
1896 Layout const & DocumentClass::getTOCLayout() const
1897 {
1898         // we're going to look for the layout with the minimum toclevel
1899         TextClass::LayoutList::const_iterator lit = begin();
1900         TextClass::LayoutList::const_iterator const len = end();
1901         int minlevel = 1000;
1902         Layout const * lay = NULL;
1903         for (; lit != len; ++lit) {
1904                 int const level = lit->toclevel;
1905                 // we don't want Part or unnumbered sections
1906                 if (level == Layout::NOT_IN_TOC || level < 0
1907                     || level >= minlevel || lit->counter.empty())
1908                         continue;
1909                 lay = &*lit;
1910                 minlevel = level;
1911         }
1912         if (lay)
1913                 return *lay;
1914         // hmm. that is very odd, so we'll do our best.
1915         return operator[](defaultLayoutName());
1916 }
1917
1918
1919 Layout const & DocumentClass::htmlTOCLayout() const
1920 {
1921         if (html_toc_section_.empty())
1922                 html_toc_section_ = getTOCLayout().name();
1923         return operator[](html_toc_section_);
1924 }
1925
1926
1927 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1928         string const & entry, bool const punct, string const & fallback) const
1929 {
1930         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%]]}";
1931         if (punct)
1932                 default_format += ".";
1933
1934         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1935         if (itype == cite_formats_.end())
1936                 return default_format;
1937         map<string, string>::const_iterator it = itype->second.find(entry);
1938         if (it == itype->second.end() && !fallback.empty())
1939                 it = itype->second.find(fallback);
1940         if (it == itype->second.end())
1941                 return default_format;
1942         if (punct)
1943                 return it->second + ".";
1944         return it->second;
1945 }
1946
1947
1948 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1949         string const & macro) const
1950 {
1951         static string empty;
1952         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1953         if (itype == cite_macros_.end())
1954                 return empty;
1955         map<string, string>::const_iterator it = itype->second.find(macro);
1956         if (it == itype->second.end())
1957                 return empty;
1958         return it->second;
1959 }
1960
1961
1962 vector<string> const DocumentClass::citeCommands(
1963         CiteEngineType const & type) const
1964 {
1965         vector<CitationStyle> const styles = citeStyles(type);
1966         vector<CitationStyle>::const_iterator it = styles.begin();
1967         vector<CitationStyle>::const_iterator end = styles.end();
1968         vector<string> cmds;
1969         for (; it != end; ++it) {
1970                 CitationStyle const cite = *it;
1971                 cmds.push_back(cite.name);
1972         }
1973         return cmds;
1974 }
1975
1976
1977 vector<CitationStyle> const & DocumentClass::citeStyles(
1978         CiteEngineType const & type) const
1979 {
1980         static vector<CitationStyle> empty;
1981         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1982         if (it == cite_styles_.end())
1983                 return empty;
1984         return it->second;
1985 }
1986
1987
1988 /////////////////////////////////////////////////////////////////////////
1989 //
1990 // PageSides
1991 //
1992 /////////////////////////////////////////////////////////////////////////
1993
1994 ostream & operator<<(ostream & os, PageSides p)
1995 {
1996         switch (p) {
1997         case OneSide:
1998                 os << '1';
1999                 break;
2000         case TwoSides:
2001                 os << '2';
2002                 break;
2003         }
2004         return os;
2005 }
2006
2007
2008 } // namespace lyx