]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
Audit all the LASSERT calls, and try to do something sensible at
[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 = 45; // rgh: New Tag "NoInsetLayout"
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         string def;
966         bool getout = false;
967         while (!getout && lexrc.isOK()) {
968                 lexrc.eatLine();
969                 def = lexrc.getString();
970                 def = subst(def, " ", "");
971                 def = subst(def, "\t", "");
972                 if (compare_ascii_no_case(def, "end") == 0) {
973                         getout = true;
974                         continue;
975                 }
976                 string cmd;
977                 CitationStyle cs;
978                 char ichar = def[0];
979                 if (ichar == '#')
980                         continue;
981                 if (ichar == 'C') {
982                         cs.forceUpperCase = true;
983                         def[0] = 'c';
984                 }
985
986                 size_t const n = def.size();
987                 for (size_t i = 0; i != n; ++i) {
988                         ichar = def[i];
989                         if (ichar == '*')
990                                 cs.fullAuthorList = true;
991                         else if (ichar == '[' && cs.textAfter)
992                                 cs.textBefore = true;
993                         else if (ichar == '[')
994                                 cs.textAfter = true;
995                         else if (ichar != ']')
996                                 cmd += ichar;
997                 }
998
999                 cs.cmd = cmd;
1000                 if (type & ENGINE_TYPE_AUTHORYEAR)
1001                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1002                 if (type & ENGINE_TYPE_NUMERICAL)
1003                         cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1004         }
1005         return getout;
1006 }
1007
1008
1009 int TextClass::readCiteEngineType(Lexer & lexrc) const
1010 {
1011         int const ENGINE_TYPE_DEFAULT =
1012                 ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL;
1013         if (!lexrc.next()) {
1014                 lexrc.printError("No cite engine type given for token: `$$Token'.");
1015                 return ENGINE_TYPE_DEFAULT;
1016         }
1017         string const type = rtrim(lexrc.getString());
1018         if (compare_ascii_no_case(type, "authoryear") == 0)
1019                 return ENGINE_TYPE_AUTHORYEAR;
1020         else if (compare_ascii_no_case(type, "numerical") == 0)
1021                 return ENGINE_TYPE_NUMERICAL;
1022         else if (compare_ascii_no_case(type, "default") != 0) {
1023                 string const s = "Unknown cite engine type `" + type
1024                         + "' given for token: `$$Token',";
1025                 lexrc.printError(s);
1026         }
1027         return ENGINE_TYPE_DEFAULT;
1028 }
1029
1030
1031 bool TextClass::readCiteFormat(Lexer & lexrc)
1032 {
1033         int const type = readCiteEngineType(lexrc);
1034         string etype;
1035         string definition;
1036         while (lexrc.isOK()) {
1037                 lexrc.next();
1038                 etype = lexrc.getString();
1039                 if (compare_ascii_no_case(etype, "end") == 0)
1040                         break;
1041                 if (!lexrc.isOK())
1042                         return false;
1043                 lexrc.eatLine();
1044                 definition = lexrc.getString();
1045                 char initchar = etype[0];
1046                 if (initchar == '#')
1047                         continue;
1048                 if (initchar == '!' || initchar == '_') {
1049                         if (type & ENGINE_TYPE_AUTHORYEAR)
1050                                 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1051                         if (type & ENGINE_TYPE_NUMERICAL)
1052                                 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1053                 } else {
1054                         if (type & ENGINE_TYPE_AUTHORYEAR)
1055                                 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1056                         if (type & ENGINE_TYPE_NUMERICAL)
1057                                 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1058                 }
1059         }
1060         return true;
1061 }
1062
1063
1064 bool TextClass::readFloat(Lexer & lexrc)
1065 {
1066         enum {
1067                 FT_TYPE = 1,
1068                 FT_NAME,
1069                 FT_PLACEMENT,
1070                 FT_EXT,
1071                 FT_WITHIN,
1072                 FT_STYLE,
1073                 FT_LISTNAME,
1074                 FT_USESFLOAT,
1075                 FT_PREDEFINED,
1076                 FT_HTMLSTYLE,
1077                 FT_HTMLATTR,
1078                 FT_HTMLTAG,
1079                 FT_LISTCOMMAND,
1080                 FT_REFPREFIX,
1081                 FT_END
1082         };
1083
1084         LexerKeyword floatTags[] = {
1085                 { "end", FT_END },
1086                 { "extension", FT_EXT },
1087                 { "guiname", FT_NAME },
1088                 { "htmlattr", FT_HTMLATTR },
1089                 { "htmlstyle", FT_HTMLSTYLE },
1090                 { "htmltag", FT_HTMLTAG },
1091                 { "ispredefined", FT_PREDEFINED },
1092                 { "listcommand", FT_LISTCOMMAND },
1093                 { "listname", FT_LISTNAME },
1094                 { "numberwithin", FT_WITHIN },
1095                 { "placement", FT_PLACEMENT },
1096                 { "refprefix", FT_REFPREFIX },
1097                 { "style", FT_STYLE },
1098                 { "type", FT_TYPE },
1099                 { "usesfloatpkg", FT_USESFLOAT }
1100         };
1101
1102         lexrc.pushTable(floatTags);
1103
1104         string ext;
1105         string htmlattr;
1106         string htmlstyle;
1107         string htmltag;
1108         string listname;
1109         string listcommand;
1110         string name;
1111         string placement;
1112         string refprefix;
1113         string style;
1114         string type;
1115         string within;
1116         bool usesfloat = true;
1117         bool ispredefined = false;
1118
1119         bool getout = false;
1120         while (!getout && lexrc.isOK()) {
1121                 int le = lexrc.lex();
1122                 switch (le) {
1123                 case Lexer::LEX_UNDEF:
1124                         lexrc.printError("Unknown float tag `$$Token'");
1125                         continue;
1126                 default:
1127                         break;
1128                 }
1129                 switch (le) {
1130                 case FT_TYPE:
1131                         lexrc.next();
1132                         type = lexrc.getString();
1133                         if (floatlist_.typeExist(type)) {
1134                                 Floating const & fl = floatlist_.getType(type);
1135                                 placement = fl.placement();
1136                                 ext = fl.ext();
1137                                 within = fl.within();
1138                                 style = fl.style();
1139                                 name = fl.name();
1140                                 listname = fl.listName();
1141                                 usesfloat = fl.usesFloatPkg();
1142                                 ispredefined = fl.isPredefined();
1143                                 listcommand = fl.listCommand();
1144                                 refprefix = fl.refPrefix();
1145                         }
1146                         break;
1147                 case FT_NAME:
1148                         lexrc.next();
1149                         name = lexrc.getString();
1150                         break;
1151                 case FT_PLACEMENT:
1152                         lexrc.next();
1153                         placement = lexrc.getString();
1154                         break;
1155                 case FT_EXT:
1156                         lexrc.next();
1157                         ext = lexrc.getString();
1158                         break;
1159                 case FT_WITHIN:
1160                         lexrc.next();
1161                         within = lexrc.getString();
1162                         if (within == "none")
1163                                 within.erase();
1164                         break;
1165                 case FT_STYLE:
1166                         lexrc.next();
1167                         style = lexrc.getString();
1168                         break;
1169                 case FT_LISTCOMMAND:
1170                         lexrc.next();
1171                         listcommand = lexrc.getString();
1172                         break;
1173                 case FT_REFPREFIX:
1174                         lexrc.next();
1175                         refprefix = lexrc.getString();
1176                         break;
1177                 case FT_LISTNAME:
1178                         lexrc.next();
1179                         listname = lexrc.getString();
1180                         break;
1181                 case FT_USESFLOAT:
1182                         lexrc.next();
1183                         usesfloat = lexrc.getBool();
1184                         break;
1185                 case FT_PREDEFINED:
1186                         lexrc.next();
1187                         ispredefined = lexrc.getBool();
1188                         break;
1189                 case FT_HTMLATTR:
1190                         lexrc.next();
1191                         htmlattr = lexrc.getString();
1192                         break;
1193                 case FT_HTMLSTYLE:
1194                         lexrc.next();
1195                         htmlstyle = lexrc.getLongString("EndHTMLStyle");
1196                         break;
1197                 case FT_HTMLTAG:
1198                         lexrc.next();
1199                         htmltag = lexrc.getString();
1200                         break;
1201                 case FT_END:
1202                         getout = true;
1203                         break;
1204                 }
1205         }
1206
1207         lexrc.popTable();
1208
1209         // Here we have a full float if getout == true
1210         if (getout) {
1211                 if (!usesfloat && listcommand.empty()) {
1212                         // if this float uses the same auxfile as an existing one,
1213                         // there is no need for it to provide a list command.
1214                         FloatList::const_iterator it = floatlist_.begin();
1215                         FloatList::const_iterator en = floatlist_.end();
1216                         bool found_ext = false;
1217                         for (; it != en; ++it) {
1218                                 if (it->second.ext() == ext) {
1219                                         found_ext = true;
1220                                         break;
1221                                 }
1222                         }
1223                         if (!found_ext)
1224                                 LYXERR0("The layout does not provide a list command " <<
1225                                   "for the float `" << type << "'. LyX will " <<
1226                                   "not be able to produce a float list.");
1227                 }
1228                 Floating fl(type, placement, ext, within, style, name,
1229                                 listname, listcommand, refprefix,
1230                                 htmltag, htmlattr, htmlstyle, usesfloat, ispredefined);
1231                 floatlist_.newFloat(fl);
1232                 // each float has its own counter
1233                 counters_.newCounter(from_ascii(type), from_ascii(within),
1234                                       docstring(), docstring());
1235                 // also define sub-float counters
1236                 docstring const subtype = "sub-" + from_ascii(type);
1237                 counters_.newCounter(subtype, from_ascii(type),
1238                                       "\\alph{" + subtype + "}", docstring());
1239         }
1240         return getout;
1241 }
1242
1243
1244 string const & TextClass::prerequisites(string const & sep) const
1245 {
1246         if (contains(prerequisites_, ',')) {
1247                 vector<string> const pres = getVectorFromString(prerequisites_);
1248                 prerequisites_ = getStringFromVector(pres, sep);
1249         }
1250         return prerequisites_;
1251 }
1252
1253
1254 bool TextClass::hasLayout(docstring const & n) const
1255 {
1256         docstring const name = n.empty() ? defaultLayoutName() : n;
1257
1258         return find_if(layoutlist_.begin(), layoutlist_.end(),
1259                        LayoutNamesEqual(name))
1260                 != layoutlist_.end();
1261 }
1262
1263
1264 bool TextClass::hasInsetLayout(docstring const & n) const
1265 {
1266         if (n.empty())
1267                 return false;
1268         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1269         return it != insetlayoutlist_.end();
1270 }
1271
1272
1273 Layout const & TextClass::operator[](docstring const & name) const
1274 {
1275         LATTEST(!name.empty());
1276
1277         const_iterator it =
1278                 find_if(begin(), end(), LayoutNamesEqual(name));
1279
1280         if (it == end()) {
1281                 LYXERR0("We failed to find the layout '" << name
1282                        << "' in the layout list. You MUST investigate!");
1283                 for (const_iterator cit = begin(); cit != end(); ++cit)
1284                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1285
1286                 // We require the name to exist
1287                 static const Layout dummy;
1288                 LASSERT(false, return dummy);
1289         }
1290
1291         return *it;
1292 }
1293
1294
1295 Layout & TextClass::operator[](docstring const & name)
1296 {
1297         LATTEST(!name.empty());
1298         // Safe to continue, given what we do below.
1299
1300         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1301
1302         if (it == end()) {
1303                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1304                        << "' in the layout list. You MUST investigate!");
1305                 for (const_iterator cit = begin(); cit != end(); ++cit)
1306                         LYXERR0(" " << to_utf8(cit->name()));
1307
1308                 // we require the name to exist
1309                 LATTEST(false);
1310                 // we are here only in release mode
1311                 layoutlist_.push_back(createBasicLayout(name, true));
1312                 it = find_if(begin(), end(), LayoutNamesEqual(name));
1313         }
1314
1315         return *it;
1316 }
1317
1318
1319 bool TextClass::deleteLayout(docstring const & name)
1320 {
1321         if (name == defaultLayoutName() || name == plainLayoutName())
1322                 return false;
1323
1324         LayoutList::iterator it =
1325                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1326                           LayoutNamesEqual(name));
1327
1328         LayoutList::iterator end = layoutlist_.end();
1329         bool const ret = (it != end);
1330         layoutlist_.erase(it, end);
1331         return ret;
1332 }
1333
1334
1335 bool TextClass::deleteInsetLayout(docstring const & name)
1336 {
1337         return insetlayoutlist_.erase(name);
1338 }
1339
1340
1341 // Load textclass info if not loaded yet
1342 bool TextClass::load(string const & path) const
1343 {
1344         if (loaded_)
1345                 return true;
1346
1347         // Read style-file, provided path is searched before the system ones
1348         // If path is a file, it is loaded directly.
1349         FileName layout_file(path);
1350         if (!path.empty() && !layout_file.isReadableFile())
1351                 layout_file = FileName(addName(path, name_ + ".layout"));
1352         if (layout_file.empty() || !layout_file.exists())
1353                 layout_file = libFileSearch("layouts", name_, "layout");
1354         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1355
1356         if (!loaded_) {
1357                 lyxerr << "Error reading `"
1358                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1359                        << "'\n(Check `" << name_
1360                        << "')\nCheck your installation and "
1361                           "try Options/Reconfigure..."
1362                        << endl;
1363         }
1364
1365         return loaded_;
1366 }
1367
1368
1369 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1370 {
1371         if (hasLayout(n))
1372                 return false;
1373
1374         layoutlist_.push_back(createBasicLayout(n, true));
1375         return true;
1376 }
1377
1378
1379 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1380 {
1381         // FIXME The fix for the InsetLayout part of 4812 would be here:
1382         // Add the InsetLayout to the document class if it is not found.
1383         docstring n = name;
1384         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1385         while (!n.empty()) {
1386                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1387                 if (cit != cen && cit->first == n)
1388                         return cit->second;
1389                 size_t i = n.find(':');
1390                 if (i == string::npos)
1391                         break;
1392                 n = n.substr(0, i);
1393         }
1394         return plain_insetlayout_;
1395 }
1396
1397
1398 docstring const & TextClass::defaultLayoutName() const
1399 {
1400         return defaultlayout_;
1401 }
1402
1403
1404 Layout const & TextClass::defaultLayout() const
1405 {
1406         return operator[](defaultLayoutName());
1407 }
1408
1409
1410 bool TextClass::isDefaultLayout(Layout const & layout) const
1411 {
1412         return layout.name() == defaultLayoutName();
1413 }
1414
1415
1416 bool TextClass::isPlainLayout(Layout const & layout) const
1417 {
1418         return layout.name() == plainLayoutName();
1419 }
1420
1421
1422 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1423 {
1424         static Layout * defaultLayout = NULL;
1425
1426         if (defaultLayout) {
1427                 defaultLayout->setUnknown(unknown);
1428                 defaultLayout->setName(name);
1429                 return *defaultLayout;
1430         }
1431
1432         static char const * s = "Margin Static\n"
1433                         "LatexType Paragraph\n"
1434                         "LatexName dummy\n"
1435                         "Align Block\n"
1436                         "AlignPossible Left, Right, Center\n"
1437                         "LabelType No_Label\n"
1438                         "End";
1439         istringstream ss(s);
1440         Lexer lex(textClassTags);
1441         lex.setStream(ss);
1442         defaultLayout = new Layout;
1443         defaultLayout->setUnknown(unknown);
1444         defaultLayout->setName(name);
1445         if (!readStyle(lex, *defaultLayout)) {
1446                 // The only way this happens is because the hardcoded layout above
1447                 // is wrong.
1448                 LATTEST(false);
1449         };
1450         return *defaultLayout;
1451 }
1452
1453
1454 DocumentClassPtr getDocumentClass(
1455                 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1456 {
1457         DocumentClassPtr doc_class =
1458             DocumentClassPtr(new DocumentClass(baseClass));
1459         LayoutModuleList::const_iterator it = modlist.begin();
1460         LayoutModuleList::const_iterator en = modlist.end();
1461         for (; it != en; ++it) {
1462                 string const modName = *it;
1463                 LyXModule * lm = theModuleList[modName];
1464                 if (!lm) {
1465                         docstring const msg =
1466                                                 bformat(_("The module %1$s has been requested by\n"
1467                                                 "this document but has not been found in the list of\n"
1468                                                 "available modules. If you recently installed it, you\n"
1469                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1470                         frontend::Alert::warning(_("Module not available"), msg);
1471                         continue;
1472                 }
1473                 if (!lm->isAvailable()) {
1474                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1475                         docstring const msg =
1476                                 bformat(_("The module %1$s requires a package that is not\n"
1477                                         "available in your LaTeX installation, or a converter that\n"
1478                                         "you have not installed. LaTeX output may not be possible.\n"
1479                                         "Missing prerequisites:\n"
1480                                                 "\t%2$s\n"
1481                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1482                                 from_utf8(modName), prereqs);
1483                         frontend::Alert::warning(_("Package not available"), msg, true);
1484                 }
1485                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1486                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1487                         docstring const msg =
1488                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1489                         frontend::Alert::warning(_("Read Error"), msg);
1490                 }
1491         }
1492         return doc_class;
1493 }
1494
1495
1496 /////////////////////////////////////////////////////////////////////////
1497 //
1498 // DocumentClass
1499 //
1500 /////////////////////////////////////////////////////////////////////////
1501
1502 DocumentClass::DocumentClass(LayoutFile const & tc)
1503         : TextClass(tc)
1504 {}
1505
1506
1507 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1508 {
1509         LayoutList::const_iterator it  = layoutlist_.begin();
1510         LayoutList::const_iterator end = layoutlist_.end();
1511         for (; it != end; ++it)
1512                 if (it->latexname() == lay)
1513                         return true;
1514         return false;
1515 }
1516
1517
1518 bool DocumentClass::provides(string const & p) const
1519 {
1520         return provides_.find(p) != provides_.end();
1521 }
1522
1523
1524 bool DocumentClass::hasTocLevels() const
1525 {
1526         return min_toclevel_ != Layout::NOT_IN_TOC;
1527 }
1528
1529
1530 Layout const & DocumentClass::getTOCLayout() const
1531 {
1532         // we're going to look for the layout with the minimum toclevel
1533         TextClass::LayoutList::const_iterator lit = begin();
1534         TextClass::LayoutList::const_iterator const len = end();
1535         int minlevel = 1000;
1536         Layout const * lay = NULL;
1537         for (; lit != len; ++lit) {
1538                 int const level = lit->toclevel;
1539                 // we don't want Part
1540                 if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1541                         continue;
1542                 lay = &*lit;
1543                 minlevel = level;
1544         }
1545         if (lay)
1546                 return *lay;
1547         // hmm. that is very odd, so we'll do our best.
1548         return operator[](defaultLayoutName());
1549 }
1550
1551
1552 Layout const & DocumentClass::htmlTOCLayout() const
1553 {
1554         if (html_toc_section_.empty())
1555                 html_toc_section_ = getTOCLayout().name();
1556         return operator[](html_toc_section_);
1557 }
1558
1559
1560 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1561         string const & entry, string const & fallback) const
1562 {
1563         static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1564
1565         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1566         if (itype == cite_formats_.end())
1567                 return default_format;
1568         map<string, string>::const_iterator it = itype->second.find(entry);
1569         if (it == itype->second.end() && !fallback.empty())
1570                 it = itype->second.find(fallback);
1571         if (it == itype->second.end())
1572                 return default_format;
1573         return it->second;
1574 }
1575
1576
1577 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1578         string const & macro) const
1579 {
1580         static string empty;
1581         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1582         if (itype == cite_macros_.end())
1583                 return empty;
1584         map<string, string>::const_iterator it = itype->second.find(macro);
1585         if (it == itype->second.end())
1586                 return empty;
1587         return it->second;
1588 }
1589
1590
1591 vector<string> const DocumentClass::citeCommands(
1592         CiteEngineType const & type) const
1593 {
1594         vector<CitationStyle> const styles = citeStyles(type);
1595         vector<CitationStyle>::const_iterator it = styles.begin();
1596         vector<CitationStyle>::const_iterator end = styles.end();
1597         vector<string> cmds;
1598         for (; it != end; ++it) {
1599                 CitationStyle const cite = *it;
1600                 cmds.push_back(cite.cmd);
1601         }
1602         return cmds;
1603 }
1604
1605
1606 vector<CitationStyle> const & DocumentClass::citeStyles(
1607         CiteEngineType const & type) const
1608 {
1609         static vector<CitationStyle> empty;
1610         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1611         if (it == cite_styles_.end())
1612                 return empty;
1613         return it->second;
1614 }
1615
1616
1617 /////////////////////////////////////////////////////////////////////////
1618 //
1619 // PageSides
1620 //
1621 /////////////////////////////////////////////////////////////////////////
1622
1623 ostream & operator<<(ostream & os, PageSides p)
1624 {
1625         switch (p) {
1626         case OneSide:
1627                 os << '1';
1628                 break;
1629         case TwoSides:
1630                 os << '2';
1631                 break;
1632         }
1633         return os;
1634 }
1635
1636
1637 } // namespace lyx