]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
9b25a67b181229b1b9ca0c83005f6f24e1ca5060
[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                                 string const inc = lexrc.getString();
467                                 FileName tmp = libFileSearch("layouts", inc,
468                                                             "layout");
469
470                                 if (tmp.empty()) {
471                                         lexrc.printError("Could not find input file: " + inc);
472                                         error = true;
473                                 } else if (!read(tmp, MERGE)) {
474                                         lexrc.printError("Error reading input file: " + tmp.absFileName());
475                                         error = true;
476                                 }
477                         }
478                         break;
479
480                 case TC_DEFAULTSTYLE:
481                         if (lexrc.next()) {
482                                 docstring const name = from_utf8(subst(lexrc.getString(),
483                                                           '_', ' '));
484                                 defaultlayout_ = name;
485                         }
486                         break;
487
488                 case TC_MODIFYSTYLE:
489                         modifystyle = true;
490                 // fall through
491                 case TC_PROVIDESTYLE:
492                         // if modifystyle is true, then we got here by falling through
493                         // so we are not in an ProvideStyle block
494                         if (!modifystyle)
495                                 providestyle = true;
496                 // fall through
497                 case TC_STYLE: {
498                         if (!lexrc.next()) {
499                                 lexrc.printError("No name given for style: `$$Token'.");
500                                 error = true;
501                                 break;
502                         }
503                         docstring const name = from_utf8(subst(lexrc.getString(),
504                                                         '_', ' '));
505                         if (name.empty()) {
506                                 string s = "Could not read name for style: `$$Token' "
507                                         + lexrc.getString() + " is probably not valid UTF-8!";
508                                 lexrc.printError(s);
509                                 Layout lay;
510                                 // Since we couldn't read the name, we just scan the rest
511                                 // of the style and discard it.
512                                 error = !readStyle(lexrc, lay);
513                                 break;
514                         }
515                         
516                         bool const have_layout = hasLayout(name);
517                         
518                         // If the layout already exists, then we want to add it to
519                         // the existing layout, as long as we are not in an ProvideStyle
520                         // block.
521                         if (have_layout && !providestyle) {
522                                 Layout & lay = operator[](name);
523                                 error = !readStyle(lexrc, lay);
524                         }
525                         // If the layout does not exist, then we want to create a new
526                         // one, but not if we are in a ModifyStyle block.
527                         else if (!have_layout && !modifystyle) {
528                                 Layout layout;
529                                 layout.setName(name);
530                                 error = !readStyle(lexrc, layout);
531                                 if (!error)
532                                         layoutlist_.push_back(layout);
533
534                                 if (defaultlayout_.empty()) {
535                                         // We do not have a default layout yet, so we choose
536                                         // the first layout we encounter.
537                                         defaultlayout_ = name;
538                                 }
539                         }
540                         // There are two ways to get here:
541                         //  (i)  The layout exists but we are in an ProvideStyle block
542                         //  (ii) The layout doesn't exist, but we are in an ModifyStyle
543                         //       block.
544                         // Either way, we just scan the rest and discard it
545                         else {
546                                 Layout lay;
547                                 // false positive from coverity
548                                 // coverity[CHECKED_RETURN]
549                                 readStyle(lexrc, lay);
550                         }
551                         break;
552                 }
553
554                 case TC_NOSTYLE:
555                         if (lexrc.next()) {
556                                 docstring const style = from_utf8(subst(lexrc.getString(),
557                                                      '_', ' '));
558                                 if (!deleteLayout(style))
559                                         lyxerr << "Cannot delete style `"
560                                                << to_utf8(style) << '\'' << endl;
561                         }
562                         break;
563
564                 case TC_NOINSETLAYOUT:
565                         if (lexrc.next()) {
566                                 docstring const style = from_utf8(subst(lexrc.getString(),
567                                                                  '_', ' '));
568                                 if (!deleteInsetLayout(style))
569                                         LYXERR0("Style `" << style << "' cannot be removed\n"
570                                                 "because it was not found!");
571                         }
572                         break;
573
574                 case TC_COLUMNS:
575                         if (lexrc.next())
576                                 columns_ = lexrc.getInteger();
577                         break;
578
579                 case TC_SIDES:
580                         if (lexrc.next()) {
581                                 switch (lexrc.getInteger()) {
582                                 case 1: sides_ = OneSide; break;
583                                 case 2: sides_ = TwoSides; break;
584                                 default:
585                                         lyxerr << "Impossible number of page"
586                                                 " sides, setting to one."
587                                                << endl;
588                                         sides_ = OneSide;
589                                         break;
590                                 }
591                         }
592                         break;
593
594                 case TC_PAGESTYLE:
595                         lexrc.next();
596                         pagestyle_ = rtrim(lexrc.getString());
597                         break;
598
599                 case TC_DEFAULTFONT:
600                         defaultfont_ = lyxRead(lexrc);
601                         if (!defaultfont_.resolved()) {
602                                 lexrc.printError("Warning: defaultfont should "
603                                                  "be fully instantiated!");
604                                 defaultfont_.realize(sane_font);
605                         }
606                         break;
607
608                 case TC_SECNUMDEPTH:
609                         lexrc.next();
610                         secnumdepth_ = lexrc.getInteger();
611                         break;
612
613                 case TC_TOCDEPTH:
614                         lexrc.next();
615                         tocdepth_ = lexrc.getInteger();
616                         break;
617
618                 // First step to support options
619                 case TC_CLASSOPTIONS:
620                         readClassOptions(lexrc);
621                         break;
622
623                 case TC_PREAMBLE:
624                         preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
625                         break;
626
627                 case TC_HTMLPREAMBLE:
628                         htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
629                         break;
630
631                 case TC_HTMLSTYLES:
632                         htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
633                         break;
634
635                 case TC_HTMLTOCSECTION:
636                         html_toc_section_ = from_utf8(trim(lexrc.getString()));
637                         break;
638
639                 case TC_ADDTOPREAMBLE:
640                         preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
641                         break;
642
643                 case TC_ADDTOHTMLPREAMBLE:
644                         htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
645                         break;
646
647                 case TC_ADDTOHTMLSTYLES:
648                         htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
649                         break;
650
651                 case TC_PROVIDES: {
652                         lexrc.next();
653                         string const feature = lexrc.getString();
654                         lexrc.next();
655                         if (lexrc.getInteger())
656                                 provides_.insert(feature);
657                         else
658                                 provides_.erase(feature);
659                         break;
660                 }
661
662                 case TC_REQUIRES: {
663                         lexrc.eatLine();
664                         vector<string> const req
665                                 = getVectorFromString(lexrc.getString());
666                         requires_.insert(req.begin(), req.end());
667                         break;
668                 }
669
670                 case TC_PKGOPTS : {
671                         lexrc.next();
672                         string const pkg = lexrc.getString();
673                         lexrc.next();
674                         string const options = lexrc.getString();
675                         package_options_[pkg] = options;
676                         break;
677                 }
678
679                 case TC_DEFAULTMODULE: {
680                         lexrc.next();
681                         string const module = lexrc.getString();
682                         if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
683                                 default_modules_.push_back(module);
684                         break;
685                 }
686
687                 case TC_PROVIDESMODULE: {
688                         lexrc.next();
689                         string const module = lexrc.getString();
690                         if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
691                                 provided_modules_.push_back(module);
692                         break;
693                 }
694
695                 case TC_EXCLUDESMODULE: {
696                         lexrc.next();
697                         string const module = lexrc.getString();
698                         // modules already have their own way to exclude other modules
699                         if (rt == MODULE) {
700                                 LYXERR0("ExcludesModule tag cannot be used in a module!");
701                                 break;
702                         }
703                         if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
704                                 excluded_modules_.push_back(module);
705                         break;
706                 }
707
708                 case TC_LEFTMARGIN:     // left margin type
709                         if (lexrc.next())
710                                 leftmargin_ = lexrc.getDocString();
711                         break;
712
713                 case TC_RIGHTMARGIN:    // right margin type
714                         if (lexrc.next())
715                                 rightmargin_ = lexrc.getDocString();
716                         break;
717
718                 case TC_INSETLAYOUT: {
719                         if (!lexrc.next()) {
720                                 lexrc.printError("No name given for InsetLayout: `$$Token'.");
721                                 error = true;
722                                 break;
723                         }
724                         docstring const name = subst(lexrc.getDocString(), '_', ' ');
725                         if (name.empty()) {
726                                 string s = "Could not read name for InsetLayout: `$$Token' "
727                                         + lexrc.getString() + " is probably not valid UTF-8!";
728                                 lexrc.printError(s);
729                                 InsetLayout il;
730                                 // Since we couldn't read the name, we just scan the rest
731                                 // of the style and discard it.
732                                 il.read(lexrc, *this);
733                                 // Let's try to continue rather than abort.
734                                 // error = true;
735                         } else if (hasInsetLayout(name)) {
736                                 InsetLayout & il = insetlayoutlist_[name];
737                                 error = !il.read(lexrc, *this);
738                         } else {
739                                 InsetLayout il;
740                                 il.setName(name);
741                                 error = !il.read(lexrc, *this);
742                                 if (!error)
743                                         insetlayoutlist_[name] = il;
744                         }
745                         break;
746                 }
747
748                 case TC_FLOAT:
749                         error = !readFloat(lexrc);
750                         break;
751
752                 case TC_CITEENGINE:
753                         error = !readCiteEngine(lexrc);
754                         break;
755
756                 case TC_CITEENGINETYPE:
757                         if (lexrc.next())
758                                 opt_enginetype_ = rtrim(lexrc.getString());
759                         break;
760
761                 case TC_CITEFORMAT:
762                         error = !readCiteFormat(lexrc);
763                         break;
764
765                 case TC_CITEFRAMEWORK:
766                         lexrc.next();
767                         citeframework_ = rtrim(lexrc.getString());
768                         break;
769
770                 case TC_MAXCITENAMES:
771                         lexrc.next();
772                         maxcitenames_ = size_t(lexrc.getInteger());
773                         break;
774
775                 case TC_DEFAULTBIBLIO:
776                         if (lexrc.next()) {
777                                 vector<string> const dbs =
778                                         getVectorFromString(rtrim(lexrc.getString()), "|");
779                                 vector<string>::const_iterator it  = dbs.begin();
780                                 vector<string>::const_iterator end = dbs.end();
781                                 for (; it != end; ++it) {
782                                         if (!contains(*it, ':')) {
783                                                 vector<string> const enginetypes =
784                                                         getVectorFromString(opt_enginetype_, "|");
785                                                 for (string const &s: enginetypes)
786                                                         cite_default_biblio_style_[s] = *it;
787                                         } else {
788                                                 string eng;
789                                                 string const db = split(*it, eng, ':');
790                                                 cite_default_biblio_style_[eng] = db;
791                                         }
792                                 }
793                         }
794                         break;
795
796                 case TC_FULLAUTHORLIST:
797                         if (lexrc.next())
798                                 cite_full_author_list_ &= lexrc.getBool();
799                         break;
800
801                 case TC_NOCOUNTER:
802                         if (lexrc.next()) {
803                                 docstring const cnt = lexrc.getDocString();
804                                 if (!counters_.remove(cnt))
805                                         LYXERR0("Unable to remove counter: " + to_utf8(cnt));
806                         }
807                         break;
808
809                 case TC_IFCOUNTER:
810                         ifcounter = true;
811                         // fall through
812                 case TC_COUNTER:
813                         if (lexrc.next()) {
814                                 docstring const name = lexrc.getDocString();
815                                 if (name.empty()) {
816                                         string s = "Could not read name for counter: `$$Token' "
817                                                         + lexrc.getString() + " is probably not valid UTF-8!";
818                                         lexrc.printError(s.c_str());
819                                         Counter c;
820                                         // Since we couldn't read the name, we just scan the rest
821                                         // and discard it.
822                                         c.read(lexrc);
823                                 } else
824                                         error = !counters_.read(lexrc, name, !ifcounter);
825                         }
826                         else {
827                                 lexrc.printError("No name given for style: `$$Token'.");
828                                 error = true;
829                         }
830                         break;
831
832                 case TC_TITLELATEXTYPE:
833                         readTitleType(lexrc);
834                         break;
835
836                 case TC_TITLELATEXNAME:
837                         if (lexrc.next())
838                                 titlename_ = lexrc.getString();
839                         break;
840
841                 case TC_NOFLOAT:
842                         if (lexrc.next()) {
843                                 string const nofloat = lexrc.getString();
844                                 floatlist_.erase(nofloat);
845                         }
846                         break;
847
848                 case TC_OUTLINERNAME:
849                         error = !readOutlinerName(lexrc);
850                         break;
851                 } // end of switch
852
853                 // Note that this is triggered the first time through the loop unless
854                 // we hit a format tag.
855                 if (format != LAYOUT_FORMAT)
856                         return FORMAT_MISMATCH;
857         }
858
859         // at present, we abort if we encounter an error,
860         // so there is no point continuing.
861         if (error)
862                 return ERROR;
863
864         if (rt != BASECLASS)
865                 return OK;
866
867         if (defaultlayout_.empty()) {
868                 LYXERR0("Error: Textclass '" << name_
869                                                 << "' is missing a defaultstyle.");
870                 return ERROR;
871         }
872
873         // Try to erase "stdinsets" from the provides_ set.
874         // The
875         //   Provides stdinsets 1
876         // declaration simply tells us that the standard insets have been
877         // defined. (It's found in stdinsets.inc but could also be used in
878         // user-defined files.) There isn't really any such package. So we
879         // might as well go ahead and erase it.
880         // If we do not succeed, then it was not there, which means that
881         // the textclass did not provide the definitions of the standard
882         // insets. So we need to try to load them.
883         int erased = provides_.erase("stdinsets");
884         if (!erased) {
885                 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
886
887                 if (tmp.empty()) {
888                         frontend::Alert::warning(_("Missing File"),
889                                 _("Could not find stdinsets.inc! This may lead to data loss!"));
890                         error = true;
891                 } else if (!read(tmp, MERGE)) {
892                         frontend::Alert::warning(_("Corrupt File"),
893                                 _("Could not read stdinsets.inc! This may lead to data loss!"));
894                         error = true;
895                 }
896         }
897
898         min_toclevel_ = Layout::NOT_IN_TOC;
899         max_toclevel_ = Layout::NOT_IN_TOC;
900         const_iterator lit = begin();
901         const_iterator len = end();
902         for (; lit != len; ++lit) {
903                 int const toclevel = lit->toclevel;
904                 if (toclevel != Layout::NOT_IN_TOC) {
905                         if (min_toclevel_ == Layout::NOT_IN_TOC)
906                                 min_toclevel_ = toclevel;
907                         else
908                                 min_toclevel_ = min(min_toclevel_, toclevel);
909                         max_toclevel_ = max(max_toclevel_, toclevel);
910                 }
911         }
912         LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
913                 << ", maximum is " << max_toclevel_);
914
915         return (error ? ERROR : OK);
916 }
917
918
919 void TextClass::readTitleType(Lexer & lexrc)
920 {
921         LexerKeyword titleTypeTags[] = {
922                 { "commandafter", TITLE_COMMAND_AFTER },
923                 { "environment",  TITLE_ENVIRONMENT }
924         };
925
926         PushPopHelper pph(lexrc, titleTypeTags);
927
928         int le = lexrc.lex();
929         switch (le) {
930         case Lexer::LEX_UNDEF:
931                 lexrc.printError("Unknown output type `$$Token'");
932                 break;
933         case TITLE_COMMAND_AFTER:
934         case TITLE_ENVIRONMENT:
935                 titletype_ = static_cast<TitleLatexType>(le);
936                 break;
937         default:
938                 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
939                 break;
940         }
941 }
942
943
944 void TextClass::readOutputType(Lexer & lexrc)
945 {
946         LexerKeyword outputTypeTags[] = {
947                 { "docbook",  DOCBOOK },
948                 { "latex",    LATEX },
949                 { "literate", LITERATE }
950         };
951
952         PushPopHelper pph(lexrc, outputTypeTags);
953
954         int le = lexrc.lex();
955         switch (le) {
956         case Lexer::LEX_UNDEF:
957                 lexrc.printError("Unknown output type `$$Token'");
958                 return;
959         case LATEX:
960         case DOCBOOK:
961         case LITERATE:
962                 outputType_ = static_cast<OutputType>(le);
963                 break;
964         default:
965                 LYXERR0("Unhandled value " << le);
966                 break;
967         }
968 }
969
970
971 void TextClass::readClassOptions(Lexer & lexrc)
972 {
973         enum {
974                 CO_FONTSIZE = 1,
975                 CO_PAGESTYLE,
976                 CO_OTHER,
977                 CO_HEADER,
978                 CO_END
979         };
980
981         LexerKeyword classOptionsTags[] = {
982                 {"end",       CO_END },
983                 {"fontsize",  CO_FONTSIZE },
984                 {"header",    CO_HEADER },
985                 {"other",     CO_OTHER },
986                 {"pagestyle", CO_PAGESTYLE }
987         };
988
989         lexrc.pushTable(classOptionsTags);
990         bool getout = false;
991         while (!getout && lexrc.isOK()) {
992                 int le = lexrc.lex();
993                 switch (le) {
994                 case Lexer::LEX_UNDEF:
995                         lexrc.printError("Unknown ClassOption tag `$$Token'");
996                         continue;
997                 default:
998                         break;
999                 }
1000                 switch (le) {
1001                 case CO_FONTSIZE:
1002                         lexrc.next();
1003                         opt_fontsize_ = rtrim(lexrc.getString());
1004                         break;
1005                 case CO_PAGESTYLE:
1006                         lexrc.next();
1007                         opt_pagestyle_ = rtrim(lexrc.getString());
1008                         break;
1009                 case CO_OTHER:
1010                         lexrc.next();
1011                         if (options_.empty())
1012                                 options_ = lexrc.getString();
1013                         else
1014                                 options_ += ',' + lexrc.getString();
1015                         break;
1016                 case CO_HEADER:
1017                         lexrc.next();
1018                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
1019                         break;
1020                 case CO_END:
1021                         getout = true;
1022                         break;
1023                 }
1024         }
1025         lexrc.popTable();
1026 }
1027
1028
1029 bool TextClass::readCiteEngine(Lexer & lexrc)
1030 {
1031         int const type = readCiteEngineType(lexrc);
1032         if (type & ENGINE_TYPE_AUTHORYEAR)
1033                 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1034         if (type & ENGINE_TYPE_NUMERICAL)
1035                 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1036         if (type & ENGINE_TYPE_DEFAULT)
1037                 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1038         string def;
1039         bool getout = false;
1040         while (!getout && lexrc.isOK()) {
1041                 lexrc.eatLine();
1042                 def = lexrc.getString();
1043                 def = subst(def, " ", "");
1044                 def = subst(def, "\t", "");
1045                 if (compare_ascii_no_case(def, "end") == 0) {
1046                         getout = true;
1047                         continue;
1048                 }
1049                 CitationStyle cs;
1050                 char ichar = def[0];
1051                 if (ichar == '#')
1052                         continue;
1053                 if (isUpperCase(ichar)) {
1054                         cs.forceUpperCase = true;
1055                         def[0] = lowercase(ichar);
1056                 }
1057
1058                 /** For portability reasons (between different
1059                  *  cite engines such as natbib and biblatex),
1060                  *  we distinguish between:
1061                  *  1. The LyX name as output in the LyX file
1062                  *  2. Possible aliases that might fall back to
1063                  *     the given LyX name in the current engine
1064                  *  3. The actual LaTeX command that is output
1065                  *  (2) and (3) are optional.
1066                  *  Also, the GUI string for the starred version can
1067                  *  be changed
1068                  *  The syntax is:
1069                  *  LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1070                  */
1071                 enum ScanMode {
1072                         LyXName,
1073                         Alias,
1074                         LaTeXCmd,
1075                         StarDesc
1076                 };
1077
1078                 ScanMode mode = LyXName;
1079                 ScanMode oldmode = LyXName;
1080                 string lyx_cmd;
1081                 string alias;
1082                 string latex_cmd;
1083                 string stardesc;
1084                 size_t const n = def.size();
1085                 for (size_t i = 0; i != n; ++i) {
1086                         ichar = def[i];
1087                         if (ichar == '|')
1088                                 mode = Alias;
1089                         else if (ichar == '=')
1090                                 mode = LaTeXCmd;
1091                         else if (ichar == '<') {
1092                                 oldmode = mode;
1093                                 mode = StarDesc;
1094                         } else if (ichar == '>')
1095                                 mode = oldmode;
1096                         else if (mode == LaTeXCmd)
1097                                 latex_cmd += ichar;
1098                         else if (mode == StarDesc)
1099                                 stardesc += ichar;
1100                         else if (ichar == '*')
1101                                 cs.hasStarredVersion = true;
1102                         else if (ichar == '[' && cs.textAfter)
1103                                 cs.textBefore = true;
1104                         else if (ichar == '[')
1105                                 cs.textAfter = true;
1106                         else if (ichar != ']') {
1107                                 if (mode == Alias)
1108                                         alias += ichar;
1109                                 else
1110                                         lyx_cmd += ichar;
1111                         }
1112                 }
1113                 cs.name = lyx_cmd;
1114                 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1115                 if (!alias.empty()) {
1116                         vector<string> const aliases = getVectorFromString(alias);
1117                         for (string const &s: aliases)
1118                                 cite_command_aliases_[s] = lyx_cmd;
1119                 }
1120                 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1121                 int size = stardesc.size();
1122                 if (size > 0)
1123                         cs.stardesc = stardescs[0];
1124                 if (size > 1)
1125                         cs.startooltip = stardescs[1];
1126                 if (type & ENGINE_TYPE_AUTHORYEAR)
1127                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1128                 if (type & ENGINE_TYPE_NUMERICAL)
1129                         cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1130                 if (type & ENGINE_TYPE_DEFAULT)
1131                         cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1132         }
1133         return getout;
1134 }
1135
1136
1137 int TextClass::readCiteEngineType(Lexer & lexrc) const
1138 {
1139         LATTEST(ENGINE_TYPE_DEFAULT ==
1140                 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1141         if (!lexrc.next()) {
1142                 lexrc.printError("No cite engine type given for token: `$$Token'.");
1143                 return ENGINE_TYPE_DEFAULT;
1144         }
1145         string const type = rtrim(lexrc.getString());
1146         if (compare_ascii_no_case(type, "authoryear") == 0)
1147                 return ENGINE_TYPE_AUTHORYEAR;
1148         else if (compare_ascii_no_case(type, "numerical") == 0)
1149                 return ENGINE_TYPE_NUMERICAL;
1150         else if (compare_ascii_no_case(type, "default") != 0) {
1151                 string const s = "Unknown cite engine type `" + type
1152                         + "' given for token: `$$Token',";
1153                 lexrc.printError(s);
1154         }
1155         return ENGINE_TYPE_DEFAULT;
1156 }
1157
1158
1159 bool TextClass::readCiteFormat(Lexer & lexrc)
1160 {
1161         int const type = readCiteEngineType(lexrc);
1162         string etype;
1163         string definition;
1164         while (lexrc.isOK()) {
1165                 lexrc.next();
1166                 etype = lexrc.getString();
1167                 if (compare_ascii_no_case(etype, "end") == 0)
1168                         break;
1169                 if (!lexrc.isOK())
1170                         return false;
1171                 lexrc.eatLine();
1172                 definition = lexrc.getString();
1173                 char initchar = etype[0];
1174                 if (initchar == '#')
1175                         continue;
1176                 if (initchar == '!' || initchar == '_') {
1177                         if (type & ENGINE_TYPE_AUTHORYEAR)
1178                                 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1179                         if (type & ENGINE_TYPE_NUMERICAL)
1180                                 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1181                         if (type & ENGINE_TYPE_DEFAULT)
1182                                 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1183                 } else {
1184                         if (type & ENGINE_TYPE_AUTHORYEAR)
1185                                 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1186                         if (type & ENGINE_TYPE_NUMERICAL)
1187                                 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1188                         if (type & ENGINE_TYPE_DEFAULT)
1189                                 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1190                 }
1191         }
1192         return true;
1193 }
1194
1195
1196 bool TextClass::readFloat(Lexer & lexrc)
1197 {
1198         enum {
1199                 FT_TYPE = 1,
1200                 FT_NAME,
1201                 FT_PLACEMENT,
1202                 FT_EXT,
1203                 FT_WITHIN,
1204                 FT_STYLE,
1205                 FT_LISTNAME,
1206                 FT_USESFLOAT,
1207                 FT_PREDEFINED,
1208                 FT_HTMLSTYLE,
1209                 FT_HTMLATTR,
1210                 FT_HTMLTAG,
1211                 FT_LISTCOMMAND,
1212                 FT_REFPREFIX,
1213                 FT_ALLOWED_PLACEMENT,
1214                 FT_ALLOWS_SIDEWAYS,
1215                 FT_ALLOWS_WIDE,
1216                 FT_END
1217         };
1218
1219         LexerKeyword floatTags[] = {
1220                 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1221                 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1222                 { "allowswide", FT_ALLOWS_WIDE },
1223                 { "end", FT_END },
1224                 { "extension", FT_EXT },
1225                 { "guiname", FT_NAME },
1226                 { "htmlattr", FT_HTMLATTR },
1227                 { "htmlstyle", FT_HTMLSTYLE },
1228                 { "htmltag", FT_HTMLTAG },
1229                 { "ispredefined", FT_PREDEFINED },
1230                 { "listcommand", FT_LISTCOMMAND },
1231                 { "listname", FT_LISTNAME },
1232                 { "numberwithin", FT_WITHIN },
1233                 { "placement", FT_PLACEMENT },
1234                 { "refprefix", FT_REFPREFIX },
1235                 { "style", FT_STYLE },
1236                 { "type", FT_TYPE },
1237                 { "usesfloatpkg", FT_USESFLOAT }
1238         };
1239
1240         lexrc.pushTable(floatTags);
1241
1242         string ext;
1243         string htmlattr;
1244         docstring htmlstyle;
1245         string htmltag;
1246         string listname;
1247         string listcommand;
1248         string name;
1249         string placement;
1250         string allowed_placement = "!htbpH";
1251         string refprefix;
1252         string style;
1253         string type;
1254         string within;
1255         bool usesfloat = true;
1256         bool ispredefined = false;
1257         bool allowswide = true;
1258         bool allowssideways = true;
1259
1260         bool getout = false;
1261         while (!getout && lexrc.isOK()) {
1262                 int le = lexrc.lex();
1263                 switch (le) {
1264                 case Lexer::LEX_UNDEF:
1265                         lexrc.printError("Unknown float tag `$$Token'");
1266                         continue;
1267                 default:
1268                         break;
1269                 }
1270                 switch (le) {
1271                 case FT_TYPE:
1272                         lexrc.next();
1273                         type = lexrc.getString();
1274                         if (floatlist_.typeExist(type)) {
1275                                 Floating const & fl = floatlist_.getType(type);
1276                                 placement = fl.placement();
1277                                 ext = fl.ext();
1278                                 within = fl.within();
1279                                 style = fl.style();
1280                                 name = fl.name();
1281                                 listname = fl.listName();
1282                                 usesfloat = fl.usesFloatPkg();
1283                                 ispredefined = fl.isPredefined();
1284                                 listcommand = fl.listCommand();
1285                                 refprefix = fl.refPrefix();
1286                         }
1287                         break;
1288                 case FT_NAME:
1289                         lexrc.next();
1290                         name = lexrc.getString();
1291                         break;
1292                 case FT_PLACEMENT:
1293                         lexrc.next();
1294                         placement = lexrc.getString();
1295                         break;
1296                 case FT_ALLOWED_PLACEMENT:
1297                         lexrc.next();
1298                         allowed_placement = lexrc.getString();
1299                         break;
1300                 case FT_EXT:
1301                         lexrc.next();
1302                         ext = lexrc.getString();
1303                         break;
1304                 case FT_WITHIN:
1305                         lexrc.next();
1306                         within = lexrc.getString();
1307                         if (within == "none")
1308                                 within.erase();
1309                         break;
1310                 case FT_STYLE:
1311                         lexrc.next();
1312                         style = lexrc.getString();
1313                         break;
1314                 case FT_LISTCOMMAND:
1315                         lexrc.next();
1316                         listcommand = lexrc.getString();
1317                         break;
1318                 case FT_REFPREFIX:
1319                         lexrc.next();
1320                         refprefix = lexrc.getString();
1321                         break;
1322                 case FT_LISTNAME:
1323                         lexrc.next();
1324                         listname = lexrc.getString();
1325                         break;
1326                 case FT_USESFLOAT:
1327                         lexrc.next();
1328                         usesfloat = lexrc.getBool();
1329                         break;
1330                 case FT_PREDEFINED:
1331                         lexrc.next();
1332                         ispredefined = lexrc.getBool();
1333                         break;
1334                 case FT_ALLOWS_SIDEWAYS:
1335                         lexrc.next();
1336                         allowssideways = lexrc.getBool();
1337                         break;
1338                 case FT_ALLOWS_WIDE:
1339                         lexrc.next();
1340                         allowswide = lexrc.getBool();
1341                         break;
1342                 case FT_HTMLATTR:
1343                         lexrc.next();
1344                         htmlattr = lexrc.getString();
1345                         break;
1346                 case FT_HTMLSTYLE:
1347                         lexrc.next();
1348                         htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1349                         break;
1350                 case FT_HTMLTAG:
1351                         lexrc.next();
1352                         htmltag = lexrc.getString();
1353                         break;
1354                 case FT_END:
1355                         getout = true;
1356                         break;
1357                 }
1358         }
1359
1360         lexrc.popTable();
1361
1362         // Here we have a full float if getout == true
1363         if (getout) {
1364                 if (!usesfloat && listcommand.empty()) {
1365                         // if this float uses the same auxfile as an existing one,
1366                         // there is no need for it to provide a list command.
1367                         FloatList::const_iterator it = floatlist_.begin();
1368                         FloatList::const_iterator en = floatlist_.end();
1369                         bool found_ext = false;
1370                         for (; it != en; ++it) {
1371                                 if (it->second.ext() == ext) {
1372                                         found_ext = true;
1373                                         break;
1374                                 }
1375                         }
1376                         if (!found_ext)
1377                                 LYXERR0("The layout does not provide a list command " <<
1378                                   "for the float `" << type << "'. LyX will " <<
1379                                   "not be able to produce a float list.");
1380                 }
1381                 Floating fl(type, placement, ext, within, style, name,
1382                             listname, listcommand, refprefix, allowed_placement,
1383                             htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1384                             allowswide, allowssideways);
1385                 floatlist_.newFloat(fl);
1386                 // each float has its own counter
1387                 counters_.newCounter(from_ascii(type), from_ascii(within),
1388                                       docstring(), docstring());
1389                 // also define sub-float counters
1390                 docstring const subtype = "sub-" + from_ascii(type);
1391                 counters_.newCounter(subtype, from_ascii(type),
1392                                       "\\alph{" + subtype + "}", docstring());
1393         }
1394         return getout;
1395 }
1396
1397
1398 bool TextClass::readOutlinerName(Lexer & lexrc)
1399 {
1400         std::string type;
1401         docstring name;
1402         if (lexrc.next())
1403                 type = lexrc.getString();
1404         else {
1405                 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1406                 return false;
1407         }
1408         if (lexrc.next())
1409                 name = lexrc.getDocString();
1410         else {
1411                 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1412                 return false;
1413         }
1414         outliner_names_[type] = name;
1415     return true;
1416 }
1417
1418
1419 string const & TextClass::prerequisites(string const & sep) const
1420 {
1421         if (contains(prerequisites_, ',')) {
1422                 vector<string> const pres = getVectorFromString(prerequisites_);
1423                 prerequisites_ = getStringFromVector(pres, sep);
1424         }
1425         return prerequisites_;
1426 }
1427
1428
1429 bool TextClass::hasLayout(docstring const & n) const
1430 {
1431         docstring const name = n.empty() ? defaultLayoutName() : n;
1432
1433         return find_if(layoutlist_.begin(), layoutlist_.end(),
1434                        LayoutNamesEqual(name))
1435                 != layoutlist_.end();
1436 }
1437
1438
1439 bool TextClass::hasInsetLayout(docstring const & n) const
1440 {
1441         if (n.empty())
1442                 return false;
1443         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1444         return it != insetlayoutlist_.end();
1445 }
1446
1447
1448 Layout const & TextClass::operator[](docstring const & name) const
1449 {
1450         LATTEST(!name.empty());
1451
1452         const_iterator it =
1453                 find_if(begin(), end(), LayoutNamesEqual(name));
1454
1455         if (it == end()) {
1456                 LYXERR0("We failed to find the layout '" << name
1457                        << "' in the layout list. You MUST investigate!");
1458                 for (const_iterator cit = begin(); cit != end(); ++cit)
1459                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1460
1461                 // We require the name to exist
1462                 static const Layout dummy;
1463                 LASSERT(false, return dummy);
1464         }
1465
1466         return *it;
1467 }
1468
1469
1470 Layout & TextClass::operator[](docstring const & name)
1471 {
1472         LATTEST(!name.empty());
1473         // Safe to continue, given what we do below.
1474
1475         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1476
1477         if (it == end()) {
1478                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1479                        << "' in the layout list. You MUST investigate!");
1480                 for (const_iterator cit = begin(); cit != end(); ++cit)
1481                         LYXERR0(" " << to_utf8(cit->name()));
1482
1483                 // we require the name to exist
1484                 LATTEST(false);
1485                 // we are here only in release mode
1486                 layoutlist_.push_back(createBasicLayout(name, true));
1487                 it = find_if(begin(), end(), LayoutNamesEqual(name));
1488         }
1489
1490         return *it;
1491 }
1492
1493
1494 bool TextClass::deleteLayout(docstring const & name)
1495 {
1496         if (name == defaultLayoutName() || name == plainLayoutName())
1497                 return false;
1498
1499         LayoutList::iterator it =
1500                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1501                           LayoutNamesEqual(name));
1502
1503         LayoutList::iterator end = layoutlist_.end();
1504         bool const ret = (it != end);
1505         layoutlist_.erase(it, end);
1506         return ret;
1507 }
1508
1509
1510 bool TextClass::deleteInsetLayout(docstring const & name)
1511 {
1512         return insetlayoutlist_.erase(name);
1513 }
1514
1515
1516 // Load textclass info if not loaded yet
1517 bool TextClass::load(string const & path) const
1518 {
1519         if (loaded_)
1520                 return true;
1521
1522         // Read style-file, provided path is searched before the system ones
1523         // If path is a file, it is loaded directly.
1524         FileName layout_file(path);
1525         if (!path.empty() && !layout_file.isReadableFile())
1526                 layout_file = FileName(addName(path, name_ + ".layout"));
1527         if (layout_file.empty() || !layout_file.exists())
1528                 layout_file = libFileSearch("layouts", name_, "layout");
1529         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1530
1531         if (!loaded_) {
1532                 lyxerr << "Error reading `"
1533                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1534                        << "'\n(Check `" << name_
1535                        << "')\nCheck your installation and "
1536                           "try Options/Reconfigure..."
1537                        << endl;
1538         }
1539
1540         return loaded_;
1541 }
1542
1543
1544 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1545 {
1546         if (hasLayout(n))
1547                 return false;
1548
1549         layoutlist_.push_back(createBasicLayout(n, true));
1550         return true;
1551 }
1552
1553
1554 string DocumentClass::forcedLayouts() const
1555 {
1556         ostringstream os;
1557         bool first = true;
1558         const_iterator const e = end();
1559         for (const_iterator i = begin(); i != e; ++i) {
1560                 if (i->forcelocal > 0) {
1561                         if (first) {
1562                                 os << "Format " << LAYOUT_FORMAT << '\n';
1563                                 first = false;
1564                         }
1565                         i->write(os);
1566                 }
1567         }
1568         return os.str();
1569 }
1570
1571
1572 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1573 {
1574         // FIXME The fix for the InsetLayout part of 4812 would be here:
1575         // Add the InsetLayout to the document class if it is not found.
1576         docstring n = name;
1577         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1578         while (!n.empty()) {
1579                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1580                 if (cit != cen && cit->first == n) {
1581                         if (cit->second.obsoleted_by().empty())
1582                                 return cit->second;
1583                         n = cit->second.obsoleted_by();
1584                         return insetLayout(n);
1585                 }
1586                 // If we have a generic prefix (e.g., "Note:"),
1587                 // try if this one alone is found.
1588                 size_t i = n.find(':');
1589                 if (i == string::npos)
1590                         break;
1591                 n = n.substr(0, i);
1592         }
1593         // Layout "name" not found.
1594         return plainInsetLayout();
1595 }
1596
1597
1598 InsetLayout const & DocumentClass::plainInsetLayout() {
1599         static const InsetLayout plain_insetlayout_;
1600         return plain_insetlayout_;
1601 }
1602
1603
1604 docstring const & TextClass::defaultLayoutName() const
1605 {
1606         return defaultlayout_;
1607 }
1608
1609
1610 Layout const & TextClass::defaultLayout() const
1611 {
1612         return operator[](defaultLayoutName());
1613 }
1614
1615
1616 bool TextClass::isDefaultLayout(Layout const & layout) const
1617 {
1618         return layout.name() == defaultLayoutName();
1619 }
1620
1621
1622 bool TextClass::isPlainLayout(Layout const & layout) const
1623 {
1624         return layout.name() == plainLayoutName();
1625 }
1626
1627
1628 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1629 {
1630         static Layout * defaultLayout = NULL;
1631
1632         if (defaultLayout) {
1633                 defaultLayout->setUnknown(unknown);
1634                 defaultLayout->setName(name);
1635                 return *defaultLayout;
1636         }
1637
1638         static char const * s = "Margin Static\n"
1639                         "LatexType Paragraph\n"
1640                         "LatexName dummy\n"
1641                         "Align Block\n"
1642                         "AlignPossible Left, Right, Center\n"
1643                         "LabelType No_Label\n"
1644                         "End";
1645         istringstream ss(s);
1646         Lexer lex(textClassTags);
1647         lex.setStream(ss);
1648         defaultLayout = new Layout;
1649         defaultLayout->setUnknown(unknown);
1650         defaultLayout->setName(name);
1651         if (!readStyle(lex, *defaultLayout)) {
1652                 // The only way this happens is because the hardcoded layout above
1653                 // is wrong.
1654                 LATTEST(false);
1655         };
1656         return *defaultLayout;
1657 }
1658
1659
1660 DocumentClassPtr getDocumentClass(
1661                 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1662                 LayoutModuleList const & celist,
1663                 bool const clone)
1664 {
1665         DocumentClassPtr doc_class =
1666             DocumentClassPtr(new DocumentClass(baseClass));
1667         LayoutModuleList::const_iterator it = modlist.begin();
1668         LayoutModuleList::const_iterator en = modlist.end();
1669         for (; it != en; ++it) {
1670                 string const modName = *it;
1671                 LyXModule * lm = theModuleList[modName];
1672                 if (!lm) {
1673                         docstring const msg =
1674                                                 bformat(_("The module %1$s has been requested by\n"
1675                                                 "this document but has not been found in the list of\n"
1676                                                 "available modules. If you recently installed it, you\n"
1677                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1678                         if (!clone)
1679                                 frontend::Alert::warning(_("Module not available"), msg);
1680                         continue;
1681                 }
1682                 if (!lm->isAvailable() && !clone) {
1683                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1684                         docstring const msg =
1685                                 bformat(_("The module %1$s requires a package that is not\n"
1686                                         "available in your LaTeX installation, or a converter that\n"
1687                                         "you have not installed. LaTeX output may not be possible.\n"
1688                                         "Missing prerequisites:\n"
1689                                                 "\t%2$s\n"
1690                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1691                                 from_utf8(modName), prereqs);
1692                         frontend::Alert::warning(_("Package not available"), msg, true);
1693                 }
1694                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1695                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1696                         docstring const msg =
1697                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1698                         frontend::Alert::warning(_("Read Error"), msg);
1699                 }
1700         }
1701
1702         LayoutModuleList::const_iterator cit = celist.begin();
1703         LayoutModuleList::const_iterator cen = celist.end();
1704         for (; cit != cen; ++cit) {
1705                 string const ceName = *cit;
1706                 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1707                 if (!ce) {
1708                         docstring const msg =
1709                                                 bformat(_("The cite engine %1$s has been requested by\n"
1710                                                 "this document but has not been found in the list of\n"
1711                                                 "available engines. If you recently installed it, you\n"
1712                                                 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1713                         if (!clone)
1714                                 frontend::Alert::warning(_("Cite Engine not available"), msg);
1715                         continue;
1716                 }
1717                 if (!ce->isAvailable() && !clone) {
1718                         docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1719                         docstring const msg =
1720                                 bformat(_("The cite engine %1$s requires a package that is not\n"
1721                                         "available in your LaTeX installation, or a converter that\n"
1722                                         "you have not installed. LaTeX output may not be possible.\n"
1723                                         "Missing prerequisites:\n"
1724                                                 "\t%2$s\n"
1725                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1726                                 from_utf8(ceName), prereqs);
1727                         frontend::Alert::warning(_("Package not available"), msg, true);
1728                 }
1729                 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1730                 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1731                         docstring const msg =
1732                                                 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1733                         frontend::Alert::warning(_("Read Error"), msg);
1734                 }
1735         }
1736
1737         return doc_class;
1738 }
1739
1740
1741 /////////////////////////////////////////////////////////////////////////
1742 //
1743 // DocumentClass
1744 //
1745 /////////////////////////////////////////////////////////////////////////
1746
1747 DocumentClass::DocumentClass(LayoutFile const & tc)
1748         : TextClass(tc)
1749 {}
1750
1751
1752 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1753 {
1754         LayoutList::const_iterator it  = layoutlist_.begin();
1755         LayoutList::const_iterator end = layoutlist_.end();
1756         for (; it != end; ++it)
1757                 if (it->latexname() == lay)
1758                         return true;
1759         return false;
1760 }
1761
1762
1763 bool DocumentClass::provides(string const & p) const
1764 {
1765         return provides_.find(p) != provides_.end();
1766 }
1767
1768
1769 bool DocumentClass::hasTocLevels() const
1770 {
1771         return min_toclevel_ != Layout::NOT_IN_TOC;
1772 }
1773
1774
1775 Layout const & DocumentClass::getTOCLayout() const
1776 {
1777         // we're going to look for the layout with the minimum toclevel
1778         TextClass::LayoutList::const_iterator lit = begin();
1779         TextClass::LayoutList::const_iterator const len = end();
1780         int minlevel = 1000;
1781         Layout const * lay = NULL;
1782         for (; lit != len; ++lit) {
1783                 int const level = lit->toclevel;
1784                 // we don't want Part or unnumbered sections
1785                 if (level == Layout::NOT_IN_TOC || level < 0 
1786                     || level >= minlevel || lit->counter.empty())
1787                         continue;
1788                 lay = &*lit;
1789                 minlevel = level;
1790         }
1791         if (lay)
1792                 return *lay;
1793         // hmm. that is very odd, so we'll do our best.
1794         return operator[](defaultLayoutName());
1795 }
1796
1797
1798 Layout const & DocumentClass::htmlTOCLayout() const
1799 {
1800         if (html_toc_section_.empty())
1801                 html_toc_section_ = getTOCLayout().name();
1802         return operator[](html_toc_section_);
1803 }
1804
1805
1806 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1807         string const & entry, bool const punct, string const & fallback) const
1808 {
1809         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%]]}";
1810         if (punct)
1811                 default_format += ".";
1812
1813         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1814         if (itype == cite_formats_.end())
1815                 return default_format;
1816         map<string, string>::const_iterator it = itype->second.find(entry);
1817         if (it == itype->second.end() && !fallback.empty())
1818                 it = itype->second.find(fallback);
1819         if (it == itype->second.end())
1820                 return default_format;
1821         if (punct)
1822                 return it->second + ".";
1823         return it->second;
1824 }
1825
1826
1827 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1828         string const & macro) const
1829 {
1830         static string empty;
1831         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1832         if (itype == cite_macros_.end())
1833                 return empty;
1834         map<string, string>::const_iterator it = itype->second.find(macro);
1835         if (it == itype->second.end())
1836                 return empty;
1837         return it->second;
1838 }
1839
1840
1841 vector<string> const DocumentClass::citeCommands(
1842         CiteEngineType const & type) const
1843 {
1844         vector<CitationStyle> const styles = citeStyles(type);
1845         vector<CitationStyle>::const_iterator it = styles.begin();
1846         vector<CitationStyle>::const_iterator end = styles.end();
1847         vector<string> cmds;
1848         for (; it != end; ++it) {
1849                 CitationStyle const cite = *it;
1850                 cmds.push_back(cite.name);
1851         }
1852         return cmds;
1853 }
1854
1855
1856 vector<CitationStyle> const & DocumentClass::citeStyles(
1857         CiteEngineType const & type) const
1858 {
1859         static vector<CitationStyle> empty;
1860         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1861         if (it == cite_styles_.end())
1862                 return empty;
1863         return it->second;
1864 }
1865
1866
1867 /////////////////////////////////////////////////////////////////////////
1868 //
1869 // PageSides
1870 //
1871 /////////////////////////////////////////////////////////////////////////
1872
1873 ostream & operator<<(ostream & os, PageSides p)
1874 {
1875         switch (p) {
1876         case OneSide:
1877                 os << '1';
1878                 break;
1879         case TwoSides:
1880                 os << '2';
1881                 break;
1882         }
1883         return os;
1884 }
1885
1886
1887 } // namespace lyx