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