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