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