]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
Provide proper fallback if a bibliography processor is not found
[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 = 66; //spitz: New layout tag AutoNests
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                         bool const validating = (rt == VALIDATION);
735                         if (name.empty()) {
736                                 string s = "Could not read name for InsetLayout: `$$Token' "
737                                         + lexrc.getString() + " is probably not valid UTF-8!";
738                                 lexrc.printError(s);
739                                 InsetLayout il;
740                                 // Since we couldn't read the name, we just scan the rest
741                                 // of the style and discard it.
742                                 il.read(lexrc, *this);
743                                 // Let's try to continue rather than abort, unless we're validating
744                                 // in which case we want to report the error
745                                 if (validating)
746                                         error = true;
747                         } else if (hasInsetLayout(name)) {
748                                 InsetLayout & il = insetlayoutlist_[name];
749                                 error = !il.read(lexrc, *this, validating);
750                         } else {
751                                 InsetLayout il;
752                                 il.setName(name);
753                                 error = !il.read(lexrc, *this, validating);
754                                 if (!error)
755                                         insetlayoutlist_[name] = il;
756                         }
757                         break;
758                 }
759
760                 case TC_FLOAT:
761                         error = !readFloat(lexrc);
762                         break;
763
764                 case TC_CITEENGINE:
765                         error = !readCiteEngine(lexrc);
766                         break;
767
768                 case TC_CITEENGINETYPE:
769                         if (lexrc.next())
770                                 opt_enginetype_ = rtrim(lexrc.getString());
771                         break;
772
773                 case TC_CITEFORMAT:
774                         error = !readCiteFormat(lexrc);
775                         break;
776
777                 case TC_CITEFRAMEWORK:
778                         lexrc.next();
779                         citeframework_ = rtrim(lexrc.getString());
780                         break;
781
782                 case TC_MAXCITENAMES:
783                         lexrc.next();
784                         maxcitenames_ = size_t(lexrc.getInteger());
785                         break;
786
787                 case TC_DEFAULTBIBLIO:
788                         if (lexrc.next()) {
789                                 vector<string> const dbs =
790                                         getVectorFromString(rtrim(lexrc.getString()), "|");
791                                 vector<string>::const_iterator it  = dbs.begin();
792                                 vector<string>::const_iterator end = dbs.end();
793                                 for (; it != end; ++it) {
794                                         if (!contains(*it, ':')) {
795                                                 vector<string> const enginetypes =
796                                                         getVectorFromString(opt_enginetype_, "|");
797                                                 for (string const &s: enginetypes)
798                                                         cite_default_biblio_style_[s] = *it;
799                                         } else {
800                                                 string eng;
801                                                 string const db = split(*it, eng, ':');
802                                                 cite_default_biblio_style_[eng] = db;
803                                         }
804                                 }
805                         }
806                         break;
807
808                 case TC_FULLAUTHORLIST:
809                         if (lexrc.next())
810                                 cite_full_author_list_ &= lexrc.getBool();
811                         break;
812
813                 case TC_NOCOUNTER:
814                         if (lexrc.next()) {
815                                 docstring const cnt = lexrc.getDocString();
816                                 if (!counters_.remove(cnt))
817                                         LYXERR0("Unable to remove counter: " + to_utf8(cnt));
818                         }
819                         break;
820
821                 case TC_IFCOUNTER:
822                         ifcounter = true;
823                         // fall through
824                 case TC_COUNTER:
825                         if (lexrc.next()) {
826                                 docstring const name = lexrc.getDocString();
827                                 if (name.empty()) {
828                                         string s = "Could not read name for counter: `$$Token' "
829                                                         + lexrc.getString() + " is probably not valid UTF-8!";
830                                         lexrc.printError(s.c_str());
831                                         Counter c;
832                                         // Since we couldn't read the name, we just scan the rest
833                                         // and discard it.
834                                         c.read(lexrc);
835                                 } else
836                                         error = !counters_.read(lexrc, name, !ifcounter);
837                         }
838                         else {
839                                 lexrc.printError("No name given for style: `$$Token'.");
840                                 error = true;
841                         }
842                         break;
843
844                 case TC_TITLELATEXTYPE:
845                         readTitleType(lexrc);
846                         break;
847
848                 case TC_TITLELATEXNAME:
849                         if (lexrc.next())
850                                 titlename_ = lexrc.getString();
851                         break;
852
853                 case TC_NOFLOAT:
854                         if (lexrc.next()) {
855                                 string const nofloat = lexrc.getString();
856                                 floatlist_.erase(nofloat);
857                         }
858                         break;
859
860                 case TC_OUTLINERNAME:
861                         error = !readOutlinerName(lexrc);
862                         break;
863                 } // end of switch
864         }
865
866         // at present, we abort if we encounter an error,
867         // so there is no point continuing.
868         if (error)
869                 return ERROR;
870
871         if (rt != BASECLASS)
872                 return OK;
873
874         if (defaultlayout_.empty()) {
875                 LYXERR0("Error: Textclass '" << name_
876                                                 << "' is missing a defaultstyle.");
877                 return ERROR;
878         }
879
880         // Try to erase "stdinsets" from the provides_ set.
881         // The
882         //   Provides stdinsets 1
883         // declaration simply tells us that the standard insets have been
884         // defined. (It's found in stdinsets.inc but could also be used in
885         // user-defined files.) There isn't really any such package. So we
886         // might as well go ahead and erase it.
887         // If we do not succeed, then it was not there, which means that
888         // the textclass did not provide the definitions of the standard
889         // insets. So we need to try to load them.
890         int erased = provides_.erase("stdinsets");
891         if (!erased) {
892                 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
893
894                 if (tmp.empty()) {
895                         frontend::Alert::warning(_("Missing File"),
896                                 _("Could not find stdinsets.inc! This may lead to data loss!"));
897                         error = true;
898                 } else if (!read(tmp, MERGE)) {
899                         frontend::Alert::warning(_("Corrupt File"),
900                                 _("Could not read stdinsets.inc! This may lead to data loss!"));
901                         error = true;
902                 }
903         }
904
905         min_toclevel_ = Layout::NOT_IN_TOC;
906         max_toclevel_ = Layout::NOT_IN_TOC;
907         const_iterator lit = begin();
908         const_iterator len = end();
909         for (; lit != len; ++lit) {
910                 int const toclevel = lit->toclevel;
911                 if (toclevel != Layout::NOT_IN_TOC) {
912                         if (min_toclevel_ == Layout::NOT_IN_TOC)
913                                 min_toclevel_ = toclevel;
914                         else
915                                 min_toclevel_ = min(min_toclevel_, toclevel);
916                         max_toclevel_ = max(max_toclevel_, toclevel);
917                 }
918         }
919         LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
920                 << ", maximum is " << max_toclevel_);
921
922         return (error ? ERROR : OK);
923 }
924
925
926 void TextClass::readTitleType(Lexer & lexrc)
927 {
928         LexerKeyword titleTypeTags[] = {
929                 { "commandafter", TITLE_COMMAND_AFTER },
930                 { "environment",  TITLE_ENVIRONMENT }
931         };
932
933         PushPopHelper pph(lexrc, titleTypeTags);
934
935         int le = lexrc.lex();
936         switch (le) {
937         case Lexer::LEX_UNDEF:
938                 lexrc.printError("Unknown output type `$$Token'");
939                 break;
940         case TITLE_COMMAND_AFTER:
941         case TITLE_ENVIRONMENT:
942                 titletype_ = static_cast<TitleLatexType>(le);
943                 break;
944         default:
945                 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
946                 break;
947         }
948 }
949
950
951 void TextClass::readOutputType(Lexer & lexrc)
952 {
953         LexerKeyword outputTypeTags[] = {
954                 { "docbook",  DOCBOOK },
955                 { "latex",    LATEX },
956                 { "literate", LITERATE }
957         };
958
959         PushPopHelper pph(lexrc, outputTypeTags);
960
961         int le = lexrc.lex();
962         switch (le) {
963         case Lexer::LEX_UNDEF:
964                 lexrc.printError("Unknown output type `$$Token'");
965                 return;
966         case LATEX:
967         case DOCBOOK:
968         case LITERATE:
969                 outputType_ = static_cast<OutputType>(le);
970                 break;
971         default:
972                 LYXERR0("Unhandled value " << le);
973                 break;
974         }
975 }
976
977
978 void TextClass::readClassOptions(Lexer & lexrc)
979 {
980         enum {
981                 CO_FONTSIZE = 1,
982                 CO_PAGESTYLE,
983                 CO_OTHER,
984                 CO_HEADER,
985                 CO_END
986         };
987
988         LexerKeyword classOptionsTags[] = {
989                 {"end",       CO_END },
990                 {"fontsize",  CO_FONTSIZE },
991                 {"header",    CO_HEADER },
992                 {"other",     CO_OTHER },
993                 {"pagestyle", CO_PAGESTYLE }
994         };
995
996         lexrc.pushTable(classOptionsTags);
997         bool getout = false;
998         while (!getout && lexrc.isOK()) {
999                 int le = lexrc.lex();
1000                 switch (le) {
1001                 case Lexer::LEX_UNDEF:
1002                         lexrc.printError("Unknown ClassOption tag `$$Token'");
1003                         continue;
1004                 default:
1005                         break;
1006                 }
1007                 switch (le) {
1008                 case CO_FONTSIZE:
1009                         lexrc.next();
1010                         opt_fontsize_ = rtrim(lexrc.getString());
1011                         break;
1012                 case CO_PAGESTYLE:
1013                         lexrc.next();
1014                         opt_pagestyle_ = rtrim(lexrc.getString());
1015                         break;
1016                 case CO_OTHER:
1017                         lexrc.next();
1018                         if (options_.empty())
1019                                 options_ = lexrc.getString();
1020                         else
1021                                 options_ += ',' + lexrc.getString();
1022                         break;
1023                 case CO_HEADER:
1024                         lexrc.next();
1025                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
1026                         break;
1027                 case CO_END:
1028                         getout = true;
1029                         break;
1030                 }
1031         }
1032         lexrc.popTable();
1033 }
1034
1035
1036 bool TextClass::readCiteEngine(Lexer & lexrc)
1037 {
1038         int const type = readCiteEngineType(lexrc);
1039         if (type & ENGINE_TYPE_AUTHORYEAR)
1040                 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1041         if (type & ENGINE_TYPE_NUMERICAL)
1042                 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1043         if (type & ENGINE_TYPE_DEFAULT)
1044                 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1045         string def;
1046         bool getout = false;
1047         while (!getout && lexrc.isOK()) {
1048                 lexrc.eatLine();
1049                 def = lexrc.getString();
1050                 def = subst(def, " ", "");
1051                 def = subst(def, "\t", "");
1052                 if (compare_ascii_no_case(def, "end") == 0) {
1053                         getout = true;
1054                         continue;
1055                 }
1056                 CitationStyle cs;
1057                 char ichar = def[0];
1058                 if (ichar == '#')
1059                         continue;
1060                 if (isUpperCase(ichar)) {
1061                         cs.forceUpperCase = true;
1062                         def[0] = lowercase(ichar);
1063                 }
1064
1065                 /** For portability reasons (between different
1066                  *  cite engines such as natbib and biblatex),
1067                  *  we distinguish between:
1068                  *  1. The LyX name as output in the LyX file
1069                  *  2. Possible aliases that might fall back to
1070                  *     the given LyX name in the current engine
1071                  *  3. The actual LaTeX command that is output
1072                  *  (2) and (3) are optional.
1073                  *  Also, the GUI string for the starred version can
1074                  *  be changed
1075                  *  The syntax is:
1076                  *  LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1077                  */
1078                 enum ScanMode {
1079                         LyXName,
1080                         Alias,
1081                         LaTeXCmd,
1082                         StarDesc
1083                 };
1084
1085                 ScanMode mode = LyXName;
1086                 ScanMode oldmode = LyXName;
1087                 string lyx_cmd;
1088                 string alias;
1089                 string latex_cmd;
1090                 string stardesc;
1091                 size_t const n = def.size();
1092                 for (size_t i = 0; i != n; ++i) {
1093                         ichar = def[i];
1094                         if (ichar == '|')
1095                                 mode = Alias;
1096                         else if (ichar == '=')
1097                                 mode = LaTeXCmd;
1098                         else if (ichar == '<') {
1099                                 oldmode = mode;
1100                                 mode = StarDesc;
1101                         } else if (ichar == '>')
1102                                 mode = oldmode;
1103                         else if (mode == LaTeXCmd)
1104                                 latex_cmd += ichar;
1105                         else if (mode == StarDesc)
1106                                 stardesc += ichar;
1107                         else if (ichar == '$')
1108                                 cs.hasQualifiedList = true;
1109                         else if (ichar == '*')
1110                                 cs.hasStarredVersion = true;
1111                         else if (ichar == '[' && cs.textAfter)
1112                                 cs.textBefore = true;
1113                         else if (ichar == '[')
1114                                 cs.textAfter = true;
1115                         else if (ichar != ']') {
1116                                 if (mode == Alias)
1117                                         alias += ichar;
1118                                 else
1119                                         lyx_cmd += ichar;
1120                         }
1121                 }
1122                 cs.name = lyx_cmd;
1123                 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1124                 if (!alias.empty()) {
1125                         vector<string> const aliases = getVectorFromString(alias);
1126                         for (string const &s: aliases)
1127                                 cite_command_aliases_[s] = lyx_cmd;
1128                 }
1129                 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1130                 int size = stardesc.size();
1131                 if (size > 0)
1132                         cs.stardesc = stardescs[0];
1133                 if (size > 1)
1134                         cs.startooltip = stardescs[1];
1135                 if (type & ENGINE_TYPE_AUTHORYEAR)
1136                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1137                 if (type & ENGINE_TYPE_NUMERICAL)
1138                         cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1139                 if (type & ENGINE_TYPE_DEFAULT)
1140                         cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1141         }
1142         return getout;
1143 }
1144
1145
1146 int TextClass::readCiteEngineType(Lexer & lexrc) const
1147 {
1148         LATTEST(ENGINE_TYPE_DEFAULT ==
1149                 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1150         if (!lexrc.next()) {
1151                 lexrc.printError("No cite engine type given for token: `$$Token'.");
1152                 return ENGINE_TYPE_DEFAULT;
1153         }
1154         string const type = rtrim(lexrc.getString());
1155         if (compare_ascii_no_case(type, "authoryear") == 0)
1156                 return ENGINE_TYPE_AUTHORYEAR;
1157         else if (compare_ascii_no_case(type, "numerical") == 0)
1158                 return ENGINE_TYPE_NUMERICAL;
1159         else if (compare_ascii_no_case(type, "default") != 0) {
1160                 string const s = "Unknown cite engine type `" + type
1161                         + "' given for token: `$$Token',";
1162                 lexrc.printError(s);
1163         }
1164         return ENGINE_TYPE_DEFAULT;
1165 }
1166
1167
1168 bool TextClass::readCiteFormat(Lexer & lexrc)
1169 {
1170         int const type = readCiteEngineType(lexrc);
1171         string etype;
1172         string definition;
1173         while (lexrc.isOK()) {
1174                 lexrc.next();
1175                 etype = lexrc.getString();
1176                 if (compare_ascii_no_case(etype, "end") == 0)
1177                         break;
1178                 if (!lexrc.isOK())
1179                         return false;
1180                 lexrc.eatLine();
1181                 definition = lexrc.getString();
1182                 char initchar = etype[0];
1183                 if (initchar == '#')
1184                         continue;
1185                 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1186                         if (type & ENGINE_TYPE_AUTHORYEAR)
1187                                 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1188                         if (type & ENGINE_TYPE_NUMERICAL)
1189                                 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1190                         if (type & ENGINE_TYPE_DEFAULT)
1191                                 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1192                 } else {
1193                         if (type & ENGINE_TYPE_AUTHORYEAR)
1194                                 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1195                         if (type & ENGINE_TYPE_NUMERICAL)
1196                                 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1197                         if (type & ENGINE_TYPE_DEFAULT)
1198                                 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1199                 }
1200         }
1201         return true;
1202 }
1203
1204
1205 bool TextClass::readFloat(Lexer & lexrc)
1206 {
1207         enum {
1208                 FT_TYPE = 1,
1209                 FT_NAME,
1210                 FT_PLACEMENT,
1211                 FT_EXT,
1212                 FT_WITHIN,
1213                 FT_STYLE,
1214                 FT_LISTNAME,
1215                 FT_USESFLOAT,
1216                 FT_PREDEFINED,
1217                 FT_HTMLSTYLE,
1218                 FT_HTMLATTR,
1219                 FT_HTMLTAG,
1220                 FT_LISTCOMMAND,
1221                 FT_REFPREFIX,
1222                 FT_ALLOWED_PLACEMENT,
1223                 FT_ALLOWS_SIDEWAYS,
1224                 FT_ALLOWS_WIDE,
1225                 FT_END
1226         };
1227
1228         LexerKeyword floatTags[] = {
1229                 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1230                 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1231                 { "allowswide", FT_ALLOWS_WIDE },
1232                 { "end", FT_END },
1233                 { "extension", FT_EXT },
1234                 { "guiname", FT_NAME },
1235                 { "htmlattr", FT_HTMLATTR },
1236                 { "htmlstyle", FT_HTMLSTYLE },
1237                 { "htmltag", FT_HTMLTAG },
1238                 { "ispredefined", FT_PREDEFINED },
1239                 { "listcommand", FT_LISTCOMMAND },
1240                 { "listname", FT_LISTNAME },
1241                 { "numberwithin", FT_WITHIN },
1242                 { "placement", FT_PLACEMENT },
1243                 { "refprefix", FT_REFPREFIX },
1244                 { "style", FT_STYLE },
1245                 { "type", FT_TYPE },
1246                 { "usesfloatpkg", FT_USESFLOAT }
1247         };
1248
1249         lexrc.pushTable(floatTags);
1250
1251         string ext;
1252         string htmlattr;
1253         docstring htmlstyle;
1254         string htmltag;
1255         string listname;
1256         string listcommand;
1257         string name;
1258         string placement;
1259         string allowed_placement = "!htbpH";
1260         string refprefix;
1261         string style;
1262         string type;
1263         string within;
1264         bool usesfloat = true;
1265         bool ispredefined = false;
1266         bool allowswide = true;
1267         bool allowssideways = true;
1268
1269         bool getout = false;
1270         while (!getout && lexrc.isOK()) {
1271                 int le = lexrc.lex();
1272                 switch (le) {
1273                 case Lexer::LEX_UNDEF:
1274                         lexrc.printError("Unknown float tag `$$Token'");
1275                         continue;
1276                 default:
1277                         break;
1278                 }
1279                 switch (le) {
1280                 case FT_TYPE:
1281                         lexrc.next();
1282                         type = lexrc.getString();
1283                         if (floatlist_.typeExist(type)) {
1284                                 Floating const & fl = floatlist_.getType(type);
1285                                 placement = fl.placement();
1286                                 ext = fl.ext();
1287                                 within = fl.within();
1288                                 style = fl.style();
1289                                 name = fl.name();
1290                                 listname = fl.listName();
1291                                 usesfloat = fl.usesFloatPkg();
1292                                 ispredefined = fl.isPredefined();
1293                                 listcommand = fl.listCommand();
1294                                 refprefix = fl.refPrefix();
1295                         }
1296                         break;
1297                 case FT_NAME:
1298                         lexrc.next();
1299                         name = lexrc.getString();
1300                         break;
1301                 case FT_PLACEMENT:
1302                         lexrc.next();
1303                         placement = lexrc.getString();
1304                         break;
1305                 case FT_ALLOWED_PLACEMENT:
1306                         lexrc.next();
1307                         allowed_placement = lexrc.getString();
1308                         break;
1309                 case FT_EXT:
1310                         lexrc.next();
1311                         ext = lexrc.getString();
1312                         break;
1313                 case FT_WITHIN:
1314                         lexrc.next();
1315                         within = lexrc.getString();
1316                         if (within == "none")
1317                                 within.erase();
1318                         break;
1319                 case FT_STYLE:
1320                         lexrc.next();
1321                         style = lexrc.getString();
1322                         break;
1323                 case FT_LISTCOMMAND:
1324                         lexrc.next();
1325                         listcommand = lexrc.getString();
1326                         break;
1327                 case FT_REFPREFIX:
1328                         lexrc.next();
1329                         refprefix = lexrc.getString();
1330                         break;
1331                 case FT_LISTNAME:
1332                         lexrc.next();
1333                         listname = lexrc.getString();
1334                         break;
1335                 case FT_USESFLOAT:
1336                         lexrc.next();
1337                         usesfloat = lexrc.getBool();
1338                         break;
1339                 case FT_PREDEFINED:
1340                         lexrc.next();
1341                         ispredefined = lexrc.getBool();
1342                         break;
1343                 case FT_ALLOWS_SIDEWAYS:
1344                         lexrc.next();
1345                         allowssideways = lexrc.getBool();
1346                         break;
1347                 case FT_ALLOWS_WIDE:
1348                         lexrc.next();
1349                         allowswide = lexrc.getBool();
1350                         break;
1351                 case FT_HTMLATTR:
1352                         lexrc.next();
1353                         htmlattr = lexrc.getString();
1354                         break;
1355                 case FT_HTMLSTYLE:
1356                         lexrc.next();
1357                         htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1358                         break;
1359                 case FT_HTMLTAG:
1360                         lexrc.next();
1361                         htmltag = lexrc.getString();
1362                         break;
1363                 case FT_END:
1364                         getout = true;
1365                         break;
1366                 }
1367         }
1368
1369         lexrc.popTable();
1370
1371         // Here we have a full float if getout == true
1372         if (getout) {
1373                 if (!usesfloat && listcommand.empty()) {
1374                         // if this float uses the same auxfile as an existing one,
1375                         // there is no need for it to provide a list command.
1376                         FloatList::const_iterator it = floatlist_.begin();
1377                         FloatList::const_iterator en = floatlist_.end();
1378                         bool found_ext = false;
1379                         for (; it != en; ++it) {
1380                                 if (it->second.ext() == ext) {
1381                                         found_ext = true;
1382                                         break;
1383                                 }
1384                         }
1385                         if (!found_ext)
1386                                 LYXERR0("The layout does not provide a list command " <<
1387                                   "for the float `" << type << "'. LyX will " <<
1388                                   "not be able to produce a float list.");
1389                 }
1390                 Floating fl(type, placement, ext, within, style, name,
1391                             listname, listcommand, refprefix, allowed_placement,
1392                             htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1393                             allowswide, allowssideways);
1394                 floatlist_.newFloat(fl);
1395                 // each float has its own counter
1396                 counters_.newCounter(from_ascii(type), from_ascii(within),
1397                                       docstring(), docstring());
1398                 // also define sub-float counters
1399                 docstring const subtype = "sub-" + from_ascii(type);
1400                 counters_.newCounter(subtype, from_ascii(type),
1401                                       "\\alph{" + subtype + "}", docstring());
1402         }
1403         return getout;
1404 }
1405
1406
1407 bool TextClass::readOutlinerName(Lexer & lexrc)
1408 {
1409         std::string type;
1410         docstring name;
1411         if (lexrc.next())
1412                 type = lexrc.getString();
1413         else {
1414                 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1415                 return false;
1416         }
1417         if (lexrc.next())
1418                 name = lexrc.getDocString();
1419         else {
1420                 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1421                 return false;
1422         }
1423         outliner_names_[type] = name;
1424     return true;
1425 }
1426
1427
1428 string const & TextClass::prerequisites(string const & sep) const
1429 {
1430         if (contains(prerequisites_, ',')) {
1431                 vector<string> const pres = getVectorFromString(prerequisites_);
1432                 prerequisites_ = getStringFromVector(pres, sep);
1433         }
1434         return prerequisites_;
1435 }
1436
1437
1438 bool TextClass::hasLayout(docstring const & n) const
1439 {
1440         docstring const name = n.empty() ? defaultLayoutName() : n;
1441
1442         return find_if(layoutlist_.begin(), layoutlist_.end(),
1443                        LayoutNamesEqual(name))
1444                 != layoutlist_.end();
1445 }
1446
1447
1448 bool TextClass::hasInsetLayout(docstring const & n) const
1449 {
1450         if (n.empty())
1451                 return false;
1452         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1453         return it != insetlayoutlist_.end();
1454 }
1455
1456
1457 Layout const & TextClass::operator[](docstring const & name) const
1458 {
1459         LATTEST(!name.empty());
1460
1461         const_iterator it =
1462                 find_if(begin(), end(), LayoutNamesEqual(name));
1463
1464         if (it == end()) {
1465                 LYXERR0("We failed to find the layout '" << name
1466                        << "' in the layout list. You MUST investigate!");
1467                 for (const_iterator cit = begin(); cit != end(); ++cit)
1468                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1469
1470                 // We require the name to exist
1471                 static const Layout dummy;
1472                 LASSERT(false, return dummy);
1473         }
1474
1475         return *it;
1476 }
1477
1478
1479 Layout & TextClass::operator[](docstring const & name)
1480 {
1481         LATTEST(!name.empty());
1482         // Safe to continue, given what we do below.
1483
1484         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1485
1486         if (it == end()) {
1487                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1488                        << "' in the layout list. You MUST investigate!");
1489                 for (const_iterator cit = begin(); cit != end(); ++cit)
1490                         LYXERR0(" " << to_utf8(cit->name()));
1491
1492                 // we require the name to exist
1493                 LATTEST(false);
1494                 // we are here only in release mode
1495                 layoutlist_.push_back(createBasicLayout(name, true));
1496                 it = find_if(begin(), end(), LayoutNamesEqual(name));
1497         }
1498
1499         return *it;
1500 }
1501
1502
1503 bool TextClass::deleteLayout(docstring const & name)
1504 {
1505         if (name == defaultLayoutName() || name == plainLayoutName())
1506                 return false;
1507
1508         LayoutList::iterator it =
1509                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1510                           LayoutNamesEqual(name));
1511
1512         LayoutList::iterator end = layoutlist_.end();
1513         bool const ret = (it != end);
1514         layoutlist_.erase(it, end);
1515         return ret;
1516 }
1517
1518
1519 bool TextClass::deleteInsetLayout(docstring const & name)
1520 {
1521         return insetlayoutlist_.erase(name);
1522 }
1523
1524
1525 // Load textclass info if not loaded yet
1526 bool TextClass::load(string const & path) const
1527 {
1528         if (loaded_)
1529                 return true;
1530
1531         // Read style-file, provided path is searched before the system ones
1532         // If path is a file, it is loaded directly.
1533         FileName layout_file(path);
1534         if (!path.empty() && !layout_file.isReadableFile())
1535                 layout_file = FileName(addName(path, name_ + ".layout"));
1536         if (layout_file.empty() || !layout_file.exists())
1537                 layout_file = libFileSearch("layouts", name_, "layout");
1538         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1539
1540         if (!loaded_) {
1541                 lyxerr << "Error reading `"
1542                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1543                        << "'\n(Check `" << name_
1544                        << "')\nCheck your installation and "
1545                           "try Options/Reconfigure..."
1546                        << endl;
1547         }
1548
1549         return loaded_;
1550 }
1551
1552
1553 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1554 {
1555         if (hasLayout(n))
1556                 return false;
1557
1558         layoutlist_.push_back(createBasicLayout(n, true));
1559         return true;
1560 }
1561
1562
1563 string DocumentClass::forcedLayouts() const
1564 {
1565         ostringstream os;
1566         bool first = true;
1567         const_iterator const e = end();
1568         for (const_iterator i = begin(); i != e; ++i) {
1569                 if (i->forcelocal > 0) {
1570                         if (first) {
1571                                 os << "Format " << LAYOUT_FORMAT << '\n';
1572                                 first = false;
1573                         }
1574                         i->write(os);
1575                 }
1576         }
1577         return os.str();
1578 }
1579
1580
1581 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1582 {
1583         // FIXME The fix for the InsetLayout part of 4812 would be here:
1584         // Add the InsetLayout to the document class if it is not found.
1585         docstring n = name;
1586         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1587         while (!n.empty()) {
1588                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1589                 if (cit != cen && cit->first == n) {
1590                         if (cit->second.obsoleted_by().empty())
1591                                 return cit->second;
1592                         n = cit->second.obsoleted_by();
1593                         return insetLayout(n);
1594                 }
1595                 // If we have a generic prefix (e.g., "Note:"),
1596                 // try if this one alone is found.
1597                 size_t i = n.find(':');
1598                 if (i == string::npos)
1599                         break;
1600                 n = n.substr(0, i);
1601         }
1602         // Layout "name" not found.
1603         return plainInsetLayout();
1604 }
1605
1606
1607 InsetLayout const & DocumentClass::plainInsetLayout() {
1608         static const InsetLayout plain_insetlayout_;
1609         return plain_insetlayout_;
1610 }
1611
1612
1613 docstring const & TextClass::defaultLayoutName() const
1614 {
1615         return defaultlayout_;
1616 }
1617
1618
1619 Layout const & TextClass::defaultLayout() const
1620 {
1621         return operator[](defaultLayoutName());
1622 }
1623
1624
1625 bool TextClass::isDefaultLayout(Layout const & layout) const
1626 {
1627         return layout.name() == defaultLayoutName();
1628 }
1629
1630
1631 bool TextClass::isPlainLayout(Layout const & layout) const
1632 {
1633         return layout.name() == plainLayoutName();
1634 }
1635
1636
1637 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1638 {
1639         static Layout * defaultLayout = NULL;
1640
1641         if (defaultLayout) {
1642                 defaultLayout->setUnknown(unknown);
1643                 defaultLayout->setName(name);
1644                 return *defaultLayout;
1645         }
1646
1647         static char const * s = "Margin Static\n"
1648                         "LatexType Paragraph\n"
1649                         "LatexName dummy\n"
1650                         "Align Block\n"
1651                         "AlignPossible Left, Right, Center\n"
1652                         "LabelType No_Label\n"
1653                         "End";
1654         istringstream ss(s);
1655         Lexer lex(textClassTags);
1656         lex.setStream(ss);
1657         defaultLayout = new Layout;
1658         defaultLayout->setUnknown(unknown);
1659         defaultLayout->setName(name);
1660         if (!readStyle(lex, *defaultLayout)) {
1661                 // The only way this happens is because the hardcoded layout above
1662                 // is wrong.
1663                 LATTEST(false);
1664         };
1665         return *defaultLayout;
1666 }
1667
1668
1669 DocumentClassPtr getDocumentClass(
1670                 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1671                 LayoutModuleList const & celist,
1672                 bool const clone)
1673 {
1674         DocumentClassPtr doc_class =
1675             DocumentClassPtr(new DocumentClass(baseClass));
1676         LayoutModuleList::const_iterator it = modlist.begin();
1677         LayoutModuleList::const_iterator en = modlist.end();
1678         for (; it != en; ++it) {
1679                 string const modName = *it;
1680                 LyXModule * lm = theModuleList[modName];
1681                 if (!lm) {
1682                         docstring const msg =
1683                                                 bformat(_("The module %1$s has been requested by\n"
1684                                                 "this document but has not been found in the list of\n"
1685                                                 "available modules. If you recently installed it, you\n"
1686                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1687                         if (!clone)
1688                                 frontend::Alert::warning(_("Module not available"), msg);
1689                         continue;
1690                 }
1691                 if (!lm->isAvailable() && !clone) {
1692                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1693                         docstring const msg =
1694                                 bformat(_("The module %1$s requires a package that is not\n"
1695                                         "available in your LaTeX installation, or a converter that\n"
1696                                         "you have not installed. LaTeX output may not be possible.\n"
1697                                         "Missing prerequisites:\n"
1698                                                 "\t%2$s\n"
1699                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1700                                 from_utf8(modName), prereqs);
1701                         frontend::Alert::warning(_("Package not available"), msg, true);
1702                 }
1703                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1704                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1705                         docstring const msg =
1706                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1707                         frontend::Alert::warning(_("Read Error"), msg);
1708                 }
1709         }
1710
1711         LayoutModuleList::const_iterator cit = celist.begin();
1712         LayoutModuleList::const_iterator cen = celist.end();
1713         for (; cit != cen; ++cit) {
1714                 string const ceName = *cit;
1715                 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1716                 if (!ce) {
1717                         docstring const msg =
1718                                                 bformat(_("The cite engine %1$s has been requested by\n"
1719                                                 "this document but has not been found in the list of\n"
1720                                                 "available engines. If you recently installed it, you\n"
1721                                                 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1722                         if (!clone)
1723                                 frontend::Alert::warning(_("Cite Engine not available"), msg);
1724                         continue;
1725                 }
1726                 if (!ce->isAvailable() && !clone) {
1727                         docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1728                         docstring const msg =
1729                                 bformat(_("The cite engine %1$s requires a package that is not\n"
1730                                         "available in your LaTeX installation, or a converter that\n"
1731                                         "you have not installed. LaTeX output may not be possible.\n"
1732                                         "Missing prerequisites:\n"
1733                                                 "\t%2$s\n"
1734                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1735                                 from_utf8(ceName), prereqs);
1736                         frontend::Alert::warning(_("Package not available"), msg, true);
1737                 }
1738                 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1739                 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1740                         docstring const msg =
1741                                                 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1742                         frontend::Alert::warning(_("Read Error"), msg);
1743                 }
1744         }
1745
1746         return doc_class;
1747 }
1748
1749
1750 /////////////////////////////////////////////////////////////////////////
1751 //
1752 // DocumentClass
1753 //
1754 /////////////////////////////////////////////////////////////////////////
1755
1756 DocumentClass::DocumentClass(LayoutFile const & tc)
1757         : TextClass(tc)
1758 {}
1759
1760
1761 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1762 {
1763         LayoutList::const_iterator it  = layoutlist_.begin();
1764         LayoutList::const_iterator end = layoutlist_.end();
1765         for (; it != end; ++it)
1766                 if (it->latexname() == lay)
1767                         return true;
1768         return false;
1769 }
1770
1771
1772 bool DocumentClass::provides(string const & p) const
1773 {
1774         return provides_.find(p) != provides_.end();
1775 }
1776
1777
1778 bool DocumentClass::hasTocLevels() const
1779 {
1780         return min_toclevel_ != Layout::NOT_IN_TOC;
1781 }
1782
1783
1784 Layout const & DocumentClass::getTOCLayout() const
1785 {
1786         // we're going to look for the layout with the minimum toclevel
1787         TextClass::LayoutList::const_iterator lit = begin();
1788         TextClass::LayoutList::const_iterator const len = end();
1789         int minlevel = 1000;
1790         Layout const * lay = NULL;
1791         for (; lit != len; ++lit) {
1792                 int const level = lit->toclevel;
1793                 // we don't want Part or unnumbered sections
1794                 if (level == Layout::NOT_IN_TOC || level < 0
1795                     || level >= minlevel || lit->counter.empty())
1796                         continue;
1797                 lay = &*lit;
1798                 minlevel = level;
1799         }
1800         if (lay)
1801                 return *lay;
1802         // hmm. that is very odd, so we'll do our best.
1803         return operator[](defaultLayoutName());
1804 }
1805
1806
1807 Layout const & DocumentClass::htmlTOCLayout() const
1808 {
1809         if (html_toc_section_.empty())
1810                 html_toc_section_ = getTOCLayout().name();
1811         return operator[](html_toc_section_);
1812 }
1813
1814
1815 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1816         string const & entry, bool const punct, string const & fallback) const
1817 {
1818         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%]]}";
1819         if (punct)
1820                 default_format += ".";
1821
1822         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1823         if (itype == cite_formats_.end())
1824                 return default_format;
1825         map<string, string>::const_iterator it = itype->second.find(entry);
1826         if (it == itype->second.end() && !fallback.empty())
1827                 it = itype->second.find(fallback);
1828         if (it == itype->second.end())
1829                 return default_format;
1830         if (punct)
1831                 return it->second + ".";
1832         return it->second;
1833 }
1834
1835
1836 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1837         string const & macro) const
1838 {
1839         static string empty;
1840         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1841         if (itype == cite_macros_.end())
1842                 return empty;
1843         map<string, string>::const_iterator it = itype->second.find(macro);
1844         if (it == itype->second.end())
1845                 return empty;
1846         return it->second;
1847 }
1848
1849
1850 vector<string> const DocumentClass::citeCommands(
1851         CiteEngineType const & type) const
1852 {
1853         vector<CitationStyle> const styles = citeStyles(type);
1854         vector<CitationStyle>::const_iterator it = styles.begin();
1855         vector<CitationStyle>::const_iterator end = styles.end();
1856         vector<string> cmds;
1857         for (; it != end; ++it) {
1858                 CitationStyle const cite = *it;
1859                 cmds.push_back(cite.name);
1860         }
1861         return cmds;
1862 }
1863
1864
1865 vector<CitationStyle> const & DocumentClass::citeStyles(
1866         CiteEngineType const & type) const
1867 {
1868         static vector<CitationStyle> empty;
1869         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1870         if (it == cite_styles_.end())
1871                 return empty;
1872         return it->second;
1873 }
1874
1875
1876 /////////////////////////////////////////////////////////////////////////
1877 //
1878 // PageSides
1879 //
1880 /////////////////////////////////////////////////////////////////////////
1881
1882 ostream & operator<<(ostream & os, PageSides p)
1883 {
1884         switch (p) {
1885         case OneSide:
1886                 os << '1';
1887                 break;
1888         case TwoSides:
1889                 os << '2';
1890                 break;
1891         }
1892         return os;
1893 }
1894
1895
1896 } // namespace lyx