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