]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
Merge branch 'master' into biblatex2
[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 docstring TextClass::outlinerName(std::string const & type) const
1420 {
1421         std::map<std::string,docstring>::const_iterator const it
1422                 = outliner_names_.find(type);
1423         if (it == outliner_names_.end()) {
1424                 LYXERR0("Missing OutlinerName for " << type << "!");
1425                 return from_utf8(type);
1426         } else
1427                 return it->second;
1428 }
1429
1430
1431 string const & TextClass::prerequisites(string const & sep) const
1432 {
1433         if (contains(prerequisites_, ',')) {
1434                 vector<string> const pres = getVectorFromString(prerequisites_);
1435                 prerequisites_ = getStringFromVector(pres, sep);
1436         }
1437         return prerequisites_;
1438 }
1439
1440
1441 bool TextClass::hasLayout(docstring const & n) const
1442 {
1443         docstring const name = n.empty() ? defaultLayoutName() : n;
1444
1445         return find_if(layoutlist_.begin(), layoutlist_.end(),
1446                        LayoutNamesEqual(name))
1447                 != layoutlist_.end();
1448 }
1449
1450
1451 bool TextClass::hasInsetLayout(docstring const & n) const
1452 {
1453         if (n.empty())
1454                 return false;
1455         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1456         return it != insetlayoutlist_.end();
1457 }
1458
1459
1460 Layout const & TextClass::operator[](docstring const & name) const
1461 {
1462         LATTEST(!name.empty());
1463
1464         const_iterator it =
1465                 find_if(begin(), end(), LayoutNamesEqual(name));
1466
1467         if (it == end()) {
1468                 LYXERR0("We failed to find the layout '" << name
1469                        << "' in the layout list. You MUST investigate!");
1470                 for (const_iterator cit = begin(); cit != end(); ++cit)
1471                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1472
1473                 // We require the name to exist
1474                 static const Layout dummy;
1475                 LASSERT(false, return dummy);
1476         }
1477
1478         return *it;
1479 }
1480
1481
1482 Layout & TextClass::operator[](docstring const & name)
1483 {
1484         LATTEST(!name.empty());
1485         // Safe to continue, given what we do below.
1486
1487         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1488
1489         if (it == end()) {
1490                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1491                        << "' in the layout list. You MUST investigate!");
1492                 for (const_iterator cit = begin(); cit != end(); ++cit)
1493                         LYXERR0(" " << to_utf8(cit->name()));
1494
1495                 // we require the name to exist
1496                 LATTEST(false);
1497                 // we are here only in release mode
1498                 layoutlist_.push_back(createBasicLayout(name, true));
1499                 it = find_if(begin(), end(), LayoutNamesEqual(name));
1500         }
1501
1502         return *it;
1503 }
1504
1505
1506 bool TextClass::deleteLayout(docstring const & name)
1507 {
1508         if (name == defaultLayoutName() || name == plainLayoutName())
1509                 return false;
1510
1511         LayoutList::iterator it =
1512                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1513                           LayoutNamesEqual(name));
1514
1515         LayoutList::iterator end = layoutlist_.end();
1516         bool const ret = (it != end);
1517         layoutlist_.erase(it, end);
1518         return ret;
1519 }
1520
1521
1522 bool TextClass::deleteInsetLayout(docstring const & name)
1523 {
1524         return insetlayoutlist_.erase(name);
1525 }
1526
1527
1528 // Load textclass info if not loaded yet
1529 bool TextClass::load(string const & path) const
1530 {
1531         if (loaded_)
1532                 return true;
1533
1534         // Read style-file, provided path is searched before the system ones
1535         // If path is a file, it is loaded directly.
1536         FileName layout_file(path);
1537         if (!path.empty() && !layout_file.isReadableFile())
1538                 layout_file = FileName(addName(path, name_ + ".layout"));
1539         if (layout_file.empty() || !layout_file.exists())
1540                 layout_file = libFileSearch("layouts", name_, "layout");
1541         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1542
1543         if (!loaded_) {
1544                 lyxerr << "Error reading `"
1545                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1546                        << "'\n(Check `" << name_
1547                        << "')\nCheck your installation and "
1548                           "try Options/Reconfigure..."
1549                        << endl;
1550         }
1551
1552         return loaded_;
1553 }
1554
1555
1556 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1557 {
1558         if (hasLayout(n))
1559                 return false;
1560
1561         layoutlist_.push_back(createBasicLayout(n, true));
1562         return true;
1563 }
1564
1565
1566 string DocumentClass::forcedLayouts() const
1567 {
1568         ostringstream os;
1569         bool first = true;
1570         const_iterator const e = end();
1571         for (const_iterator i = begin(); i != e; ++i) {
1572                 if (i->forcelocal > 0) {
1573                         if (first) {
1574                                 os << "Format " << LAYOUT_FORMAT << '\n';
1575                                 first = false;
1576                         }
1577                         i->write(os);
1578                 }
1579         }
1580         return os.str();
1581 }
1582
1583
1584 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1585 {
1586         // FIXME The fix for the InsetLayout part of 4812 would be here:
1587         // Add the InsetLayout to the document class if it is not found.
1588         docstring n = name;
1589         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1590         while (!n.empty()) {
1591                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1592                 if (cit != cen && cit->first == n) {
1593                         if (cit->second.obsoleted_by().empty())
1594                                 return cit->second;
1595                         n = cit->second.obsoleted_by();
1596                         return insetLayout(n);
1597                 }
1598                 // If we have a generic prefix (e.g., "Note:"),
1599                 // try if this one alone is found.
1600                 size_t i = n.find(':');
1601                 if (i == string::npos)
1602                         break;
1603                 n = n.substr(0, i);
1604         }
1605         // Layout "name" not found.
1606         return plainInsetLayout();
1607 }
1608
1609
1610 InsetLayout const & DocumentClass::plainInsetLayout() {
1611         static const InsetLayout plain_insetlayout_;
1612         return plain_insetlayout_;
1613 }
1614
1615
1616 docstring const & TextClass::defaultLayoutName() const
1617 {
1618         return defaultlayout_;
1619 }
1620
1621
1622 Layout const & TextClass::defaultLayout() const
1623 {
1624         return operator[](defaultLayoutName());
1625 }
1626
1627
1628 bool TextClass::isDefaultLayout(Layout const & layout) const
1629 {
1630         return layout.name() == defaultLayoutName();
1631 }
1632
1633
1634 bool TextClass::isPlainLayout(Layout const & layout) const
1635 {
1636         return layout.name() == plainLayoutName();
1637 }
1638
1639
1640 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1641 {
1642         static Layout * defaultLayout = NULL;
1643
1644         if (defaultLayout) {
1645                 defaultLayout->setUnknown(unknown);
1646                 defaultLayout->setName(name);
1647                 return *defaultLayout;
1648         }
1649
1650         static char const * s = "Margin Static\n"
1651                         "LatexType Paragraph\n"
1652                         "LatexName dummy\n"
1653                         "Align Block\n"
1654                         "AlignPossible Left, Right, Center\n"
1655                         "LabelType No_Label\n"
1656                         "End";
1657         istringstream ss(s);
1658         Lexer lex(textClassTags);
1659         lex.setStream(ss);
1660         defaultLayout = new Layout;
1661         defaultLayout->setUnknown(unknown);
1662         defaultLayout->setName(name);
1663         if (!readStyle(lex, *defaultLayout)) {
1664                 // The only way this happens is because the hardcoded layout above
1665                 // is wrong.
1666                 LATTEST(false);
1667         };
1668         return *defaultLayout;
1669 }
1670
1671
1672 DocumentClassPtr getDocumentClass(
1673                 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1674                 LayoutModuleList const & celist,
1675                 bool const clone)
1676 {
1677         DocumentClassPtr doc_class =
1678             DocumentClassPtr(new DocumentClass(baseClass));
1679         LayoutModuleList::const_iterator it = modlist.begin();
1680         LayoutModuleList::const_iterator en = modlist.end();
1681         for (; it != en; ++it) {
1682                 string const modName = *it;
1683                 LyXModule * lm = theModuleList[modName];
1684                 if (!lm) {
1685                         docstring const msg =
1686                                                 bformat(_("The module %1$s has been requested by\n"
1687                                                 "this document but has not been found in the list of\n"
1688                                                 "available modules. If you recently installed it, you\n"
1689                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1690                         if (!clone)
1691                                 frontend::Alert::warning(_("Module not available"), msg);
1692                         continue;
1693                 }
1694                 if (!lm->isAvailable() && !clone) {
1695                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1696                         docstring const msg =
1697                                 bformat(_("The module %1$s requires a package that is not\n"
1698                                         "available in your LaTeX installation, or a converter that\n"
1699                                         "you have not installed. LaTeX output may not be possible.\n"
1700                                         "Missing prerequisites:\n"
1701                                                 "\t%2$s\n"
1702                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1703                                 from_utf8(modName), prereqs);
1704                         frontend::Alert::warning(_("Package not available"), msg, true);
1705                 }
1706                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1707                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1708                         docstring const msg =
1709                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1710                         frontend::Alert::warning(_("Read Error"), msg);
1711                 }
1712         }
1713
1714         LayoutModuleList::const_iterator cit = celist.begin();
1715         LayoutModuleList::const_iterator cen = celist.end();
1716         for (; cit != cen; ++cit) {
1717                 string const ceName = *cit;
1718                 LyXCiteEngine * ce = theCiteEnginesList[ceName];
1719                 if (!ce) {
1720                         docstring const msg =
1721                                                 bformat(_("The cite engine %1$s has been requested by\n"
1722                                                 "this document but has not been found in the list of\n"
1723                                                 "available engines. If you recently installed it, you\n"
1724                                                 "probably need to reconfigure LyX.\n"), from_utf8(ceName));
1725                         if (!clone)
1726                                 frontend::Alert::warning(_("Cite Engine not available"), msg);
1727                         continue;
1728                 }
1729                 if (!ce->isAvailable() && !clone) {
1730                         docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1731                         docstring const msg =
1732                                 bformat(_("The cite engine %1$s requires a package that is not\n"
1733                                         "available in your LaTeX installation, or a converter that\n"
1734                                         "you have not installed. LaTeX output may not be possible.\n"
1735                                         "Missing prerequisites:\n"
1736                                                 "\t%2$s\n"
1737                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1738                                 from_utf8(ceName), prereqs);
1739                         frontend::Alert::warning(_("Package not available"), msg, true);
1740                 }
1741                 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1742                 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1743                         docstring const msg =
1744                                                 bformat(_("Error reading cite engine %1$s\n"), from_utf8(ceName));
1745                         frontend::Alert::warning(_("Read Error"), msg);
1746                 }
1747         }
1748
1749         return doc_class;
1750 }
1751
1752
1753 /////////////////////////////////////////////////////////////////////////
1754 //
1755 // DocumentClass
1756 //
1757 /////////////////////////////////////////////////////////////////////////
1758
1759 DocumentClass::DocumentClass(LayoutFile const & tc)
1760         : TextClass(tc)
1761 {}
1762
1763
1764 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1765 {
1766         LayoutList::const_iterator it  = layoutlist_.begin();
1767         LayoutList::const_iterator end = layoutlist_.end();
1768         for (; it != end; ++it)
1769                 if (it->latexname() == lay)
1770                         return true;
1771         return false;
1772 }
1773
1774
1775 bool DocumentClass::provides(string const & p) const
1776 {
1777         return provides_.find(p) != provides_.end();
1778 }
1779
1780
1781 bool DocumentClass::hasTocLevels() const
1782 {
1783         return min_toclevel_ != Layout::NOT_IN_TOC;
1784 }
1785
1786
1787 Layout const & DocumentClass::getTOCLayout() const
1788 {
1789         // we're going to look for the layout with the minimum toclevel
1790         TextClass::LayoutList::const_iterator lit = begin();
1791         TextClass::LayoutList::const_iterator const len = end();
1792         int minlevel = 1000;
1793         Layout const * lay = NULL;
1794         for (; lit != len; ++lit) {
1795                 int const level = lit->toclevel;
1796                 // we don't want Part or unnumbered sections
1797                 if (level == Layout::NOT_IN_TOC || level < 0 
1798                     || level >= minlevel || lit->counter.empty())
1799                         continue;
1800                 lay = &*lit;
1801                 minlevel = level;
1802         }
1803         if (lay)
1804                 return *lay;
1805         // hmm. that is very odd, so we'll do our best.
1806         return operator[](defaultLayoutName());
1807 }
1808
1809
1810 Layout const & DocumentClass::htmlTOCLayout() const
1811 {
1812         if (html_toc_section_.empty())
1813                 html_toc_section_ = getTOCLayout().name();
1814         return operator[](html_toc_section_);
1815 }
1816
1817
1818 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
1819         string const & entry, bool const punct, string const & fallback) const
1820 {
1821         string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
1822         if (punct)
1823                 default_format += ".";
1824
1825         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1826         if (itype == cite_formats_.end())
1827                 return default_format;
1828         map<string, string>::const_iterator it = itype->second.find(entry);
1829         if (it == itype->second.end() && !fallback.empty())
1830                 it = itype->second.find(fallback);
1831         if (it == itype->second.end())
1832                 return default_format;
1833         if (punct)
1834                 return it->second + ".";
1835         return it->second;
1836 }
1837
1838
1839 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1840         string const & macro) const
1841 {
1842         static string empty;
1843         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1844         if (itype == cite_macros_.end())
1845                 return empty;
1846         map<string, string>::const_iterator it = itype->second.find(macro);
1847         if (it == itype->second.end())
1848                 return empty;
1849         return it->second;
1850 }
1851
1852
1853 vector<string> const DocumentClass::citeCommands(
1854         CiteEngineType const & type) const
1855 {
1856         vector<CitationStyle> const styles = citeStyles(type);
1857         vector<CitationStyle>::const_iterator it = styles.begin();
1858         vector<CitationStyle>::const_iterator end = styles.end();
1859         vector<string> cmds;
1860         for (; it != end; ++it) {
1861                 CitationStyle const cite = *it;
1862                 cmds.push_back(cite.name);
1863         }
1864         return cmds;
1865 }
1866
1867
1868 vector<CitationStyle> const & DocumentClass::citeStyles(
1869         CiteEngineType const & type) const
1870 {
1871         static vector<CitationStyle> empty;
1872         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1873         if (it == cite_styles_.end())
1874                 return empty;
1875         return it->second;
1876 }
1877
1878
1879 /////////////////////////////////////////////////////////////////////////
1880 //
1881 // PageSides
1882 //
1883 /////////////////////////////////////////////////////////////////////////
1884
1885 ostream & operator<<(ostream & os, PageSides p)
1886 {
1887         switch (p) {
1888         case OneSide:
1889                 os << '1';
1890                 break;
1891         case TwoSides:
1892                 os << '2';
1893                 break;
1894         }
1895         return os;
1896 }
1897
1898
1899 } // namespace lyx