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