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