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