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