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