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