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