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