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