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