]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
Update ANNOUNCE
[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                                 readStyle(lexrc, lay);
532                         }
533                         break;
534                 }
535
536                 case TC_NOSTYLE:
537                         if (lexrc.next()) {
538                                 docstring const style = from_utf8(subst(lexrc.getString(),
539                                                      '_', ' '));
540                                 if (!deleteLayout(style))
541                                         lyxerr << "Cannot delete style `"
542                                                << to_utf8(style) << '\'' << endl;
543                         }
544                         break;
545
546                 case TC_NOINSETLAYOUT:
547                         if (lexrc.next()) {
548                                 docstring const style = from_utf8(subst(lexrc.getString(),
549                                                                  '_', ' '));
550                                 if (!deleteInsetLayout(style))
551                                         LYXERR0("Style `" << style << "' cannot be removed\n"
552                                                 "because it was not found!");
553                         }
554                         break;
555
556                 case TC_COLUMNS:
557                         if (lexrc.next())
558                                 columns_ = lexrc.getInteger();
559                         break;
560
561                 case TC_SIDES:
562                         if (lexrc.next()) {
563                                 switch (lexrc.getInteger()) {
564                                 case 1: sides_ = OneSide; break;
565                                 case 2: sides_ = TwoSides; break;
566                                 default:
567                                         lyxerr << "Impossible number of page"
568                                                 " sides, setting to one."
569                                                << endl;
570                                         sides_ = OneSide;
571                                         break;
572                                 }
573                         }
574                         break;
575
576                 case TC_PAGESTYLE:
577                         lexrc.next();
578                         pagestyle_ = rtrim(lexrc.getString());
579                         break;
580
581                 case TC_DEFAULTFONT:
582                         defaultfont_ = lyxRead(lexrc);
583                         if (!defaultfont_.resolved()) {
584                                 lexrc.printError("Warning: defaultfont should "
585                                                  "be fully instantiated!");
586                                 defaultfont_.realize(sane_font);
587                         }
588                         break;
589
590                 case TC_SECNUMDEPTH:
591                         lexrc.next();
592                         secnumdepth_ = lexrc.getInteger();
593                         break;
594
595                 case TC_TOCDEPTH:
596                         lexrc.next();
597                         tocdepth_ = lexrc.getInteger();
598                         break;
599
600                 // First step to support options
601                 case TC_CLASSOPTIONS:
602                         readClassOptions(lexrc);
603                         break;
604
605                 case TC_PREAMBLE:
606                         preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
607                         break;
608
609                 case TC_HTMLPREAMBLE:
610                         htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
611                         break;
612
613                 case TC_HTMLSTYLES:
614                         htmlstyles_ = from_utf8(lexrc.getLongString("EndStyles"));
615                         break;
616
617                 case TC_HTMLTOCSECTION:
618                         html_toc_section_ = from_utf8(trim(lexrc.getString()));
619                         break;
620
621                 case TC_ADDTOPREAMBLE:
622                         preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
623                         break;
624
625                 case TC_ADDTOHTMLPREAMBLE:
626                         htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
627                         break;
628
629                 case TC_ADDTOHTMLSTYLES:
630                         htmlstyles_ += from_utf8(lexrc.getLongString("EndStyles"));
631                         break;
632
633                 case TC_PROVIDES: {
634                         lexrc.next();
635                         string const feature = lexrc.getString();
636                         lexrc.next();
637                         if (lexrc.getInteger())
638                                 provides_.insert(feature);
639                         else
640                                 provides_.erase(feature);
641                         break;
642                 }
643
644                 case TC_REQUIRES: {
645                         lexrc.eatLine();
646                         vector<string> const req
647                                 = getVectorFromString(lexrc.getString());
648                         requires_.insert(req.begin(), req.end());
649                         break;
650                 }
651
652                 case TC_PKGOPTS : {
653                         lexrc.next();
654                         string const pkg = lexrc.getString();
655                         lexrc.next();
656                         string const options = lexrc.getString();
657                         package_options_[pkg] = options;
658                         break;
659                 }
660
661                 case TC_DEFAULTMODULE: {
662                         lexrc.next();
663                         string const module = lexrc.getString();
664                         if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
665                                 default_modules_.push_back(module);
666                         break;
667                 }
668
669                 case TC_PROVIDESMODULE: {
670                         lexrc.next();
671                         string const module = lexrc.getString();
672                         if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
673                                 provided_modules_.push_back(module);
674                         break;
675                 }
676
677                 case TC_EXCLUDESMODULE: {
678                         lexrc.next();
679                         string const module = lexrc.getString();
680                         // modules already have their own way to exclude other modules
681                         if (rt == MODULE) {
682                                 LYXERR0("ExcludesModule tag cannot be used in a module!");
683                                 break;
684                         }
685                         if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
686                                 excluded_modules_.push_back(module);
687                         break;
688                 }
689
690                 case TC_LEFTMARGIN:     // left margin type
691                         if (lexrc.next())
692                                 leftmargin_ = lexrc.getDocString();
693                         break;
694
695                 case TC_RIGHTMARGIN:    // right margin type
696                         if (lexrc.next())
697                                 rightmargin_ = lexrc.getDocString();
698                         break;
699
700                 case TC_INSETLAYOUT: {
701                         if (!lexrc.next()) {
702                                 lexrc.printError("No name given for InsetLayout: `$$Token'.");
703                                 error = true;
704                                 break;
705                         }
706                         docstring const name = subst(lexrc.getDocString(), '_', ' ');
707                         if (name.empty()) {
708                                 string s = "Could not read name for InsetLayout: `$$Token' "
709                                         + lexrc.getString() + " is probably not valid UTF-8!";
710                                 lexrc.printError(s);
711                                 InsetLayout il;
712                                 // Since we couldn't read the name, we just scan the rest
713                                 // of the style and discard it.
714                                 il.read(lexrc, *this);
715                                 // Let's try to continue rather than abort.
716                                 // error = true;
717                         } else if (hasInsetLayout(name)) {
718                                 InsetLayout & il = insetlayoutlist_[name];
719                                 error = !il.read(lexrc, *this);
720                         } else {
721                                 InsetLayout il;
722                                 il.setName(name);
723                                 error = !il.read(lexrc, *this);
724                                 if (!error)
725                                         insetlayoutlist_[name] = il;
726                         }
727                         break;
728                 }
729
730                 case TC_FLOAT:
731                         error = !readFloat(lexrc);
732                         break;
733
734                 case TC_CITEENGINE:
735                         error = !readCiteEngine(lexrc);
736                         break;
737
738                 case TC_CITEENGINETYPE:
739                         if (lexrc.next())
740                                 opt_enginetype_ = rtrim(lexrc.getString());
741                         break;
742
743                 case TC_CITEFORMAT:
744                         error = !readCiteFormat(lexrc);
745                         break;
746
747                 case TC_DEFAULTBIBLIO:
748                         if (lexrc.next())
749                                 cite_default_biblio_style_ = rtrim(lexrc.getString());
750                         break;
751
752                 case TC_FULLAUTHORLIST:
753                         if (lexrc.next())
754                                 cite_full_author_list_ &= lexrc.getBool();
755                         break;
756
757                 case TC_NOCOUNTER:
758                         if (lexrc.next()) {
759                                 docstring const cnt = lexrc.getDocString();
760                                 if (!counters_.remove(cnt))
761                                         LYXERR0("Unable to remove counter: " + to_utf8(cnt));
762                         }
763                         break;
764
765                 case TC_IFCOUNTER:
766                         ifcounter = true;
767                         // fall through
768                 case TC_COUNTER:
769                         if (lexrc.next()) {
770                                 docstring const name = lexrc.getDocString();
771                                 if (name.empty()) {
772                                         string s = "Could not read name for counter: `$$Token' "
773                                                         + lexrc.getString() + " is probably not valid UTF-8!";
774                                         lexrc.printError(s.c_str());
775                                         Counter c;
776                                         // Since we couldn't read the name, we just scan the rest
777                                         // and discard it.
778                                         c.read(lexrc);
779                                 } else
780                                         error = !counters_.read(lexrc, name, !ifcounter);
781                         }
782                         else {
783                                 lexrc.printError("No name given for style: `$$Token'.");
784                                 error = true;
785                         }
786                         break;
787
788                 case TC_TITLELATEXTYPE:
789                         readTitleType(lexrc);
790                         break;
791
792                 case TC_TITLELATEXNAME:
793                         if (lexrc.next())
794                                 titlename_ = lexrc.getString();
795                         break;
796
797                 case TC_NOFLOAT:
798                         if (lexrc.next()) {
799                                 string const nofloat = lexrc.getString();
800                                 floatlist_.erase(nofloat);
801                         }
802                         break;
803
804                 case TC_OUTLINERNAME:
805                         error = !readOutlinerName(lexrc);
806                         break;
807                 } // end of switch
808
809                 // Note that this is triggered the first time through the loop unless
810                 // we hit a format tag.
811                 if (format != LAYOUT_FORMAT)
812                         return FORMAT_MISMATCH;
813         }
814
815         // at present, we abort if we encounter an error,
816         // so there is no point continuing.
817         if (error)
818                 return ERROR;
819
820         if (rt != BASECLASS)
821                 return OK;
822
823         if (defaultlayout_.empty()) {
824                 LYXERR0("Error: Textclass '" << name_
825                                                 << "' is missing a defaultstyle.");
826                 return ERROR;
827         }
828
829         // Try to erase "stdinsets" from the provides_ set.
830         // The
831         //   Provides stdinsets 1
832         // declaration simply tells us that the standard insets have been
833         // defined. (It's found in stdinsets.inc but could also be used in
834         // user-defined files.) There isn't really any such package. So we
835         // might as well go ahead and erase it.
836         // If we do not succeed, then it was not there, which means that
837         // the textclass did not provide the definitions of the standard
838         // insets. So we need to try to load them.
839         int erased = provides_.erase("stdinsets");
840         if (!erased) {
841                 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
842
843                 if (tmp.empty()) {
844                         frontend::Alert::warning(_("Missing File"),
845                                 _("Could not find stdinsets.inc! This may lead to data loss!"));
846                         error = true;
847                 } else if (!read(tmp, MERGE)) {
848                         frontend::Alert::warning(_("Corrupt File"),
849                                 _("Could not read stdinsets.inc! This may lead to data loss!"));
850                         error = true;
851                 }
852         }
853
854         min_toclevel_ = Layout::NOT_IN_TOC;
855         max_toclevel_ = Layout::NOT_IN_TOC;
856         const_iterator lit = begin();
857         const_iterator len = end();
858         for (; lit != len; ++lit) {
859                 int const toclevel = lit->toclevel;
860                 if (toclevel != Layout::NOT_IN_TOC) {
861                         if (min_toclevel_ == Layout::NOT_IN_TOC)
862                                 min_toclevel_ = toclevel;
863                         else
864                                 min_toclevel_ = min(min_toclevel_, toclevel);
865                         max_toclevel_ = max(max_toclevel_, toclevel);
866                 }
867         }
868         LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
869                 << ", maximum is " << max_toclevel_);
870
871         return (error ? ERROR : OK);
872 }
873
874
875 void TextClass::readTitleType(Lexer & lexrc)
876 {
877         LexerKeyword titleTypeTags[] = {
878                 { "commandafter", TITLE_COMMAND_AFTER },
879                 { "environment",  TITLE_ENVIRONMENT }
880         };
881
882         PushPopHelper pph(lexrc, titleTypeTags);
883
884         int le = lexrc.lex();
885         switch (le) {
886         case Lexer::LEX_UNDEF:
887                 lexrc.printError("Unknown output type `$$Token'");
888                 break;
889         case TITLE_COMMAND_AFTER:
890         case TITLE_ENVIRONMENT:
891                 titletype_ = static_cast<TitleLatexType>(le);
892                 break;
893         default:
894                 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
895                 break;
896         }
897 }
898
899
900 void TextClass::readOutputType(Lexer & lexrc)
901 {
902         LexerKeyword outputTypeTags[] = {
903                 { "docbook",  DOCBOOK },
904                 { "latex",    LATEX },
905                 { "literate", LITERATE }
906         };
907
908         PushPopHelper pph(lexrc, outputTypeTags);
909
910         int le = lexrc.lex();
911         switch (le) {
912         case Lexer::LEX_UNDEF:
913                 lexrc.printError("Unknown output type `$$Token'");
914                 return;
915         case LATEX:
916         case DOCBOOK:
917         case LITERATE:
918                 outputType_ = static_cast<OutputType>(le);
919                 break;
920         default:
921                 LYXERR0("Unhandled value " << le);
922                 break;
923         }
924 }
925
926
927 void TextClass::readClassOptions(Lexer & lexrc)
928 {
929         enum {
930                 CO_FONTSIZE = 1,
931                 CO_PAGESTYLE,
932                 CO_OTHER,
933                 CO_HEADER,
934                 CO_END
935         };
936
937         LexerKeyword classOptionsTags[] = {
938                 {"end",       CO_END },
939                 {"fontsize",  CO_FONTSIZE },
940                 {"header",    CO_HEADER },
941                 {"other",     CO_OTHER },
942                 {"pagestyle", CO_PAGESTYLE }
943         };
944
945         lexrc.pushTable(classOptionsTags);
946         bool getout = false;
947         while (!getout && lexrc.isOK()) {
948                 int le = lexrc.lex();
949                 switch (le) {
950                 case Lexer::LEX_UNDEF:
951                         lexrc.printError("Unknown ClassOption tag `$$Token'");
952                         continue;
953                 default:
954                         break;
955                 }
956                 switch (le) {
957                 case CO_FONTSIZE:
958                         lexrc.next();
959                         opt_fontsize_ = rtrim(lexrc.getString());
960                         break;
961                 case CO_PAGESTYLE:
962                         lexrc.next();
963                         opt_pagestyle_ = rtrim(lexrc.getString());
964                         break;
965                 case CO_OTHER:
966                         lexrc.next();
967                         if (options_.empty())
968                                 options_ = lexrc.getString();
969                         else
970                                 options_ += ',' + lexrc.getString();
971                         break;
972                 case CO_HEADER:
973                         lexrc.next();
974                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
975                         break;
976                 case CO_END:
977                         getout = true;
978                         break;
979                 }
980         }
981         lexrc.popTable();
982 }
983
984
985 bool TextClass::readCiteEngine(Lexer & lexrc)
986 {
987         int const type = readCiteEngineType(lexrc);
988         if (type & ENGINE_TYPE_AUTHORYEAR)
989                 cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
990         if (type & ENGINE_TYPE_NUMERICAL)
991                 cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
992         if (type & ENGINE_TYPE_DEFAULT)
993                 cite_styles_[ENGINE_TYPE_DEFAULT].clear();
994         string def;
995         bool getout = false;
996         while (!getout && lexrc.isOK()) {
997                 lexrc.eatLine();
998                 def = lexrc.getString();
999                 def = subst(def, " ", "");
1000                 def = subst(def, "\t", "");
1001                 if (compare_ascii_no_case(def, "end") == 0) {
1002                         getout = true;
1003                         continue;
1004                 }
1005                 string cmd;
1006                 CitationStyle cs;
1007                 char ichar = def[0];
1008                 if (ichar == '#')
1009                         continue;
1010                 if (ichar == 'C') {
1011                         cs.forceUpperCase = true;
1012                         def[0] = 'c';
1013                 }
1014
1015                 size_t const n = def.size();
1016                 for (size_t i = 0; i != n; ++i) {
1017                         ichar = def[i];
1018                         if (ichar == '*')
1019                                 cs.fullAuthorList = true;
1020                         else if (ichar == '[' && cs.textAfter)
1021                                 cs.textBefore = true;
1022                         else if (ichar == '[')
1023                                 cs.textAfter = true;
1024                         else if (ichar != ']')
1025                                 cmd += ichar;
1026                 }
1027
1028                 cs.cmd = cmd;
1029                 if (type & ENGINE_TYPE_AUTHORYEAR)
1030                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1031                 if (type & ENGINE_TYPE_NUMERICAL)
1032                         cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1033                 if (type & ENGINE_TYPE_DEFAULT)
1034                         cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1035         }
1036         return getout;
1037 }
1038
1039
1040 int TextClass::readCiteEngineType(Lexer & lexrc) const
1041 {
1042         LATTEST(ENGINE_TYPE_DEFAULT ==
1043                 (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL));
1044         if (!lexrc.next()) {
1045                 lexrc.printError("No cite engine type given for token: `$$Token'.");
1046                 return ENGINE_TYPE_DEFAULT;
1047         }
1048         string const type = rtrim(lexrc.getString());
1049         if (compare_ascii_no_case(type, "authoryear") == 0)
1050                 return ENGINE_TYPE_AUTHORYEAR;
1051         else if (compare_ascii_no_case(type, "numerical") == 0)
1052                 return ENGINE_TYPE_NUMERICAL;
1053         else if (compare_ascii_no_case(type, "default") != 0) {
1054                 string const s = "Unknown cite engine type `" + type
1055                         + "' given for token: `$$Token',";
1056                 lexrc.printError(s);
1057         }
1058         return ENGINE_TYPE_DEFAULT;
1059 }
1060
1061
1062 bool TextClass::readCiteFormat(Lexer & lexrc)
1063 {
1064         int const type = readCiteEngineType(lexrc);
1065         string etype;
1066         string definition;
1067         while (lexrc.isOK()) {
1068                 lexrc.next();
1069                 etype = lexrc.getString();
1070                 if (compare_ascii_no_case(etype, "end") == 0)
1071                         break;
1072                 if (!lexrc.isOK())
1073                         return false;
1074                 lexrc.eatLine();
1075                 definition = lexrc.getString();
1076                 char initchar = etype[0];
1077                 if (initchar == '#')
1078                         continue;
1079                 if (initchar == '!' || initchar == '_') {
1080                         if (type & ENGINE_TYPE_AUTHORYEAR)
1081                                 cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1082                         if (type & ENGINE_TYPE_NUMERICAL)
1083                                 cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1084                         if (type & ENGINE_TYPE_DEFAULT)
1085                                 cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1086                 } else {
1087                         if (type & ENGINE_TYPE_AUTHORYEAR)
1088                                 cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1089                         if (type & ENGINE_TYPE_NUMERICAL)
1090                                 cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1091                         if (type & ENGINE_TYPE_DEFAULT)
1092                                 cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1093                 }
1094         }
1095         return true;
1096 }
1097
1098
1099 bool TextClass::readFloat(Lexer & lexrc)
1100 {
1101         enum {
1102                 FT_TYPE = 1,
1103                 FT_NAME,
1104                 FT_PLACEMENT,
1105                 FT_EXT,
1106                 FT_WITHIN,
1107                 FT_STYLE,
1108                 FT_LISTNAME,
1109                 FT_USESFLOAT,
1110                 FT_PREDEFINED,
1111                 FT_HTMLSTYLE,
1112                 FT_HTMLATTR,
1113                 FT_HTMLTAG,
1114                 FT_LISTCOMMAND,
1115                 FT_REFPREFIX,
1116                 FT_ALLOWED_PLACEMENT,
1117                 FT_ALLOWS_SIDEWAYS,
1118                 FT_ALLOWS_WIDE,
1119                 FT_END
1120         };
1121
1122         LexerKeyword floatTags[] = {
1123                 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1124                 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1125                 { "allowswide", FT_ALLOWS_WIDE },
1126                 { "end", FT_END },
1127                 { "extension", FT_EXT },
1128                 { "guiname", FT_NAME },
1129                 { "htmlattr", FT_HTMLATTR },
1130                 { "htmlstyle", FT_HTMLSTYLE },
1131                 { "htmltag", FT_HTMLTAG },
1132                 { "ispredefined", FT_PREDEFINED },
1133                 { "listcommand", FT_LISTCOMMAND },
1134                 { "listname", FT_LISTNAME },
1135                 { "numberwithin", FT_WITHIN },
1136                 { "placement", FT_PLACEMENT },
1137                 { "refprefix", FT_REFPREFIX },
1138                 { "style", FT_STYLE },
1139                 { "type", FT_TYPE },
1140                 { "usesfloatpkg", FT_USESFLOAT }
1141         };
1142
1143         lexrc.pushTable(floatTags);
1144
1145         string ext;
1146         string htmlattr;
1147         string htmlstyle;
1148         string htmltag;
1149         string listname;
1150         string listcommand;
1151         string name;
1152         string placement;
1153         string allowed_placement = "!htbpH";
1154         string refprefix;
1155         string style;
1156         string type;
1157         string within;
1158         bool usesfloat = true;
1159         bool ispredefined = false;
1160         bool allowswide = true;
1161         bool allowssideways = true;
1162
1163         bool getout = false;
1164         while (!getout && lexrc.isOK()) {
1165                 int le = lexrc.lex();
1166                 switch (le) {
1167                 case Lexer::LEX_UNDEF:
1168                         lexrc.printError("Unknown float tag `$$Token'");
1169                         continue;
1170                 default:
1171                         break;
1172                 }
1173                 switch (le) {
1174                 case FT_TYPE:
1175                         lexrc.next();
1176                         type = lexrc.getString();
1177                         if (floatlist_.typeExist(type)) {
1178                                 Floating const & fl = floatlist_.getType(type);
1179                                 placement = fl.placement();
1180                                 ext = fl.ext();
1181                                 within = fl.within();
1182                                 style = fl.style();
1183                                 name = fl.name();
1184                                 listname = fl.listName();
1185                                 usesfloat = fl.usesFloatPkg();
1186                                 ispredefined = fl.isPredefined();
1187                                 listcommand = fl.listCommand();
1188                                 refprefix = fl.refPrefix();
1189                         }
1190                         break;
1191                 case FT_NAME:
1192                         lexrc.next();
1193                         name = lexrc.getString();
1194                         break;
1195                 case FT_PLACEMENT:
1196                         lexrc.next();
1197                         placement = lexrc.getString();
1198                         break;
1199                 case FT_ALLOWED_PLACEMENT:
1200                         lexrc.next();
1201                         allowed_placement = lexrc.getString();
1202                         break;
1203                 case FT_EXT:
1204                         lexrc.next();
1205                         ext = lexrc.getString();
1206                         break;
1207                 case FT_WITHIN:
1208                         lexrc.next();
1209                         within = lexrc.getString();
1210                         if (within == "none")
1211                                 within.erase();
1212                         break;
1213                 case FT_STYLE:
1214                         lexrc.next();
1215                         style = lexrc.getString();
1216                         break;
1217                 case FT_LISTCOMMAND:
1218                         lexrc.next();
1219                         listcommand = lexrc.getString();
1220                         break;
1221                 case FT_REFPREFIX:
1222                         lexrc.next();
1223                         refprefix = lexrc.getString();
1224                         break;
1225                 case FT_LISTNAME:
1226                         lexrc.next();
1227                         listname = lexrc.getString();
1228                         break;
1229                 case FT_USESFLOAT:
1230                         lexrc.next();
1231                         usesfloat = lexrc.getBool();
1232                         break;
1233                 case FT_PREDEFINED:
1234                         lexrc.next();
1235                         ispredefined = lexrc.getBool();
1236                         break;
1237                 case FT_ALLOWS_SIDEWAYS:
1238                         lexrc.next();
1239                         allowssideways = lexrc.getBool();
1240                         break;
1241                 case FT_ALLOWS_WIDE:
1242                         lexrc.next();
1243                         allowswide = lexrc.getBool();
1244                         break;
1245                 case FT_HTMLATTR:
1246                         lexrc.next();
1247                         htmlattr = lexrc.getString();
1248                         break;
1249                 case FT_HTMLSTYLE:
1250                         lexrc.next();
1251                         htmlstyle = lexrc.getLongString("EndHTMLStyle");
1252                         break;
1253                 case FT_HTMLTAG:
1254                         lexrc.next();
1255                         htmltag = lexrc.getString();
1256                         break;
1257                 case FT_END:
1258                         getout = true;
1259                         break;
1260                 }
1261         }
1262
1263         lexrc.popTable();
1264
1265         // Here we have a full float if getout == true
1266         if (getout) {
1267                 if (!usesfloat && listcommand.empty()) {
1268                         // if this float uses the same auxfile as an existing one,
1269                         // there is no need for it to provide a list command.
1270                         FloatList::const_iterator it = floatlist_.begin();
1271                         FloatList::const_iterator en = floatlist_.end();
1272                         bool found_ext = false;
1273                         for (; it != en; ++it) {
1274                                 if (it->second.ext() == ext) {
1275                                         found_ext = true;
1276                                         break;
1277                                 }
1278                         }
1279                         if (!found_ext)
1280                                 LYXERR0("The layout does not provide a list command " <<
1281                                   "for the float `" << type << "'. LyX will " <<
1282                                   "not be able to produce a float list.");
1283                 }
1284                 Floating fl(type, placement, ext, within, style, name,
1285                             listname, listcommand, refprefix, allowed_placement,
1286                             htmltag, htmlattr, htmlstyle, usesfloat, ispredefined,
1287                             allowswide, allowssideways);
1288                 floatlist_.newFloat(fl);
1289                 // each float has its own counter
1290                 counters_.newCounter(from_ascii(type), from_ascii(within),
1291                                       docstring(), docstring());
1292                 // also define sub-float counters
1293                 docstring const subtype = "sub-" + from_ascii(type);
1294                 counters_.newCounter(subtype, from_ascii(type),
1295                                       "\\alph{" + subtype + "}", docstring());
1296         }
1297         return getout;
1298 }
1299
1300
1301 bool TextClass::readOutlinerName(Lexer & lexrc)
1302 {
1303         std::string type;
1304         docstring name;
1305         if (lexrc.next())
1306                 type = lexrc.getString();
1307         else {
1308                 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1309                 return false;
1310         }
1311         if (lexrc.next())
1312                 name = lexrc.getDocString();
1313         else {
1314                 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1315                 return false;
1316         }
1317         outliner_names_[type] = name;
1318     return true;
1319 }
1320
1321
1322 docstring TextClass::outlinerName(std::string const & type) const
1323 {
1324         std::map<std::string,docstring>::const_iterator const it
1325                 = outliner_names_.find(type);
1326         if (it == outliner_names_.end()) {
1327                 LYXERR0("Missing OutlinerName for " << type << "!");
1328                 return from_utf8(type);
1329         } else
1330                 return it->second;
1331 }
1332
1333
1334 string const & TextClass::prerequisites(string const & sep) const
1335 {
1336         if (contains(prerequisites_, ',')) {
1337                 vector<string> const pres = getVectorFromString(prerequisites_);
1338                 prerequisites_ = getStringFromVector(pres, sep);
1339         }
1340         return prerequisites_;
1341 }
1342
1343
1344 bool TextClass::hasLayout(docstring const & n) const
1345 {
1346         docstring const name = n.empty() ? defaultLayoutName() : n;
1347
1348         return find_if(layoutlist_.begin(), layoutlist_.end(),
1349                        LayoutNamesEqual(name))
1350                 != layoutlist_.end();
1351 }
1352
1353
1354 bool TextClass::hasInsetLayout(docstring const & n) const
1355 {
1356         if (n.empty())
1357                 return false;
1358         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1359         return it != insetlayoutlist_.end();
1360 }
1361
1362
1363 Layout const & TextClass::operator[](docstring const & name) const
1364 {
1365         LATTEST(!name.empty());
1366
1367         const_iterator it =
1368                 find_if(begin(), end(), LayoutNamesEqual(name));
1369
1370         if (it == end()) {
1371                 LYXERR0("We failed to find the layout '" << name
1372                        << "' in the layout list. You MUST investigate!");
1373                 for (const_iterator cit = begin(); cit != end(); ++cit)
1374                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1375
1376                 // We require the name to exist
1377                 static const Layout dummy;
1378                 LASSERT(false, return dummy);
1379         }
1380
1381         return *it;
1382 }
1383
1384
1385 Layout & TextClass::operator[](docstring const & name)
1386 {
1387         LATTEST(!name.empty());
1388         // Safe to continue, given what we do below.
1389
1390         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1391
1392         if (it == end()) {
1393                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1394                        << "' in the layout list. You MUST investigate!");
1395                 for (const_iterator cit = begin(); cit != end(); ++cit)
1396                         LYXERR0(" " << to_utf8(cit->name()));
1397
1398                 // we require the name to exist
1399                 LATTEST(false);
1400                 // we are here only in release mode
1401                 layoutlist_.push_back(createBasicLayout(name, true));
1402                 it = find_if(begin(), end(), LayoutNamesEqual(name));
1403         }
1404
1405         return *it;
1406 }
1407
1408
1409 bool TextClass::deleteLayout(docstring const & name)
1410 {
1411         if (name == defaultLayoutName() || name == plainLayoutName())
1412                 return false;
1413
1414         LayoutList::iterator it =
1415                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1416                           LayoutNamesEqual(name));
1417
1418         LayoutList::iterator end = layoutlist_.end();
1419         bool const ret = (it != end);
1420         layoutlist_.erase(it, end);
1421         return ret;
1422 }
1423
1424
1425 bool TextClass::deleteInsetLayout(docstring const & name)
1426 {
1427         return insetlayoutlist_.erase(name);
1428 }
1429
1430
1431 // Load textclass info if not loaded yet
1432 bool TextClass::load(string const & path) const
1433 {
1434         if (loaded_)
1435                 return true;
1436
1437         // Read style-file, provided path is searched before the system ones
1438         // If path is a file, it is loaded directly.
1439         FileName layout_file(path);
1440         if (!path.empty() && !layout_file.isReadableFile())
1441                 layout_file = FileName(addName(path, name_ + ".layout"));
1442         if (layout_file.empty() || !layout_file.exists())
1443                 layout_file = libFileSearch("layouts", name_, "layout");
1444         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1445
1446         if (!loaded_) {
1447                 lyxerr << "Error reading `"
1448                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1449                        << "'\n(Check `" << name_
1450                        << "')\nCheck your installation and "
1451                           "try Options/Reconfigure..."
1452                        << endl;
1453         }
1454
1455         return loaded_;
1456 }
1457
1458
1459 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1460 {
1461         if (hasLayout(n))
1462                 return false;
1463
1464         layoutlist_.push_back(createBasicLayout(n, true));
1465         return true;
1466 }
1467
1468
1469 string DocumentClass::forcedLayouts() const
1470 {
1471         ostringstream os;
1472         bool first = true;
1473         const_iterator const e = end();
1474         for (const_iterator i = begin(); i != e; ++i) {
1475                 if (i->forcelocal > 0) {
1476                         if (first) {
1477                                 os << "Format " << LAYOUT_FORMAT << '\n';
1478                                 first = false;
1479                         }
1480                         i->write(os);
1481                 }
1482         }
1483         return os.str();
1484 }
1485
1486
1487 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1488 {
1489         // FIXME The fix for the InsetLayout part of 4812 would be here:
1490         // Add the InsetLayout to the document class if it is not found.
1491         docstring n = name;
1492         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1493         while (!n.empty()) {
1494                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1495                 if (cit != cen && cit->first == n) {
1496                         if (cit->second.obsoleted_by().empty())
1497                                 return cit->second;
1498                         n = cit->second.obsoleted_by();
1499                         return insetLayout(n);
1500                 }
1501                 // If we have a generic prefix (e.g., "Note:"),
1502                 // try if this one alone is found.
1503                 size_t i = n.find(':');
1504                 if (i == string::npos)
1505                         break;
1506                 n = n.substr(0, i);
1507         }
1508         // Layout "name" not found.
1509         return plainInsetLayout();
1510 }
1511
1512
1513 InsetLayout const & DocumentClass::plainInsetLayout() {
1514         static const InsetLayout plain_insetlayout_;
1515         return plain_insetlayout_;
1516 }
1517
1518
1519 docstring const & TextClass::defaultLayoutName() const
1520 {
1521         return defaultlayout_;
1522 }
1523
1524
1525 Layout const & TextClass::defaultLayout() const
1526 {
1527         return operator[](defaultLayoutName());
1528 }
1529
1530
1531 bool TextClass::isDefaultLayout(Layout const & layout) const
1532 {
1533         return layout.name() == defaultLayoutName();
1534 }
1535
1536
1537 bool TextClass::isPlainLayout(Layout const & layout) const
1538 {
1539         return layout.name() == plainLayoutName();
1540 }
1541
1542
1543 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1544 {
1545         static Layout * defaultLayout = NULL;
1546
1547         if (defaultLayout) {
1548                 defaultLayout->setUnknown(unknown);
1549                 defaultLayout->setName(name);
1550                 return *defaultLayout;
1551         }
1552
1553         static char const * s = "Margin Static\n"
1554                         "LatexType Paragraph\n"
1555                         "LatexName dummy\n"
1556                         "Align Block\n"
1557                         "AlignPossible Left, Right, Center\n"
1558                         "LabelType No_Label\n"
1559                         "End";
1560         istringstream ss(s);
1561         Lexer lex(textClassTags);
1562         lex.setStream(ss);
1563         defaultLayout = new Layout;
1564         defaultLayout->setUnknown(unknown);
1565         defaultLayout->setName(name);
1566         if (!readStyle(lex, *defaultLayout)) {
1567                 // The only way this happens is because the hardcoded layout above
1568                 // is wrong.
1569                 LATTEST(false);
1570         };
1571         return *defaultLayout;
1572 }
1573
1574
1575 DocumentClassPtr getDocumentClass(
1576                 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1577                 bool const clone)
1578 {
1579         DocumentClassPtr doc_class =
1580             DocumentClassPtr(new DocumentClass(baseClass));
1581         LayoutModuleList::const_iterator it = modlist.begin();
1582         LayoutModuleList::const_iterator en = modlist.end();
1583         for (; it != en; ++it) {
1584                 string const modName = *it;
1585                 LyXModule * lm = theModuleList[modName];
1586                 if (!lm) {
1587                         docstring const msg =
1588                                                 bformat(_("The module %1$s has been requested by\n"
1589                                                 "this document but has not been found in the list of\n"
1590                                                 "available modules. If you recently installed it, you\n"
1591                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1592                         if (!clone)
1593                                 frontend::Alert::warning(_("Module not available"), msg);
1594                         continue;
1595                 }
1596                 if (!lm->isAvailable() && !clone) {
1597                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1598                         docstring const msg =
1599                                 bformat(_("The module %1$s requires a package that is not\n"
1600                                         "available in your LaTeX installation, or a converter that\n"
1601                                         "you have not installed. LaTeX output may not be possible.\n"
1602                                         "Missing prerequisites:\n"
1603                                                 "\t%2$s\n"
1604                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1605                                 from_utf8(modName), prereqs);
1606                         frontend::Alert::warning(_("Package not available"), msg, true);
1607                 }
1608                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1609                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1610                         docstring const msg =
1611                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1612                         frontend::Alert::warning(_("Read Error"), msg);
1613                 }
1614         }
1615         return doc_class;
1616 }
1617
1618
1619 /////////////////////////////////////////////////////////////////////////
1620 //
1621 // DocumentClass
1622 //
1623 /////////////////////////////////////////////////////////////////////////
1624
1625 DocumentClass::DocumentClass(LayoutFile const & tc)
1626         : TextClass(tc)
1627 {}
1628
1629
1630 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1631 {
1632         LayoutList::const_iterator it  = layoutlist_.begin();
1633         LayoutList::const_iterator end = layoutlist_.end();
1634         for (; it != end; ++it)
1635                 if (it->latexname() == lay)
1636                         return true;
1637         return false;
1638 }
1639
1640
1641 bool DocumentClass::provides(string const & p) const
1642 {
1643         return provides_.find(p) != provides_.end();
1644 }
1645
1646
1647 bool DocumentClass::hasTocLevels() const
1648 {
1649         return min_toclevel_ != Layout::NOT_IN_TOC;
1650 }
1651
1652
1653 Layout const & DocumentClass::getTOCLayout() const
1654 {
1655         // we're going to look for the layout with the minimum toclevel
1656         TextClass::LayoutList::const_iterator lit = begin();
1657         TextClass::LayoutList::const_iterator const len = end();
1658         int minlevel = 1000;
1659         Layout const * lay = NULL;
1660         for (; lit != len; ++lit) {
1661                 int const level = lit->toclevel;
1662                 // we don't want Part or unnumbered sections
1663                 if (level == Layout::NOT_IN_TOC || level < 0 
1664                     || level >= minlevel || lit->counter.empty())
1665                         continue;
1666                 lay = &*lit;
1667                 minlevel = level;
1668         }
1669         if (lay)
1670                 return *lay;
1671         // hmm. that is very odd, so we'll do our best.
1672         return operator[](defaultLayoutName());
1673 }
1674
1675
1676 Layout const & DocumentClass::htmlTOCLayout() const
1677 {
1678         if (html_toc_section_.empty())
1679                 html_toc_section_ = getTOCLayout().name();
1680         return operator[](html_toc_section_);
1681 }
1682
1683
1684 string const & DocumentClass::getCiteFormat(CiteEngineType const & type,
1685         string const & entry, string const & fallback) const
1686 {
1687         static string default_format = "{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.";
1688
1689         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
1690         if (itype == cite_formats_.end())
1691                 return default_format;
1692         map<string, string>::const_iterator it = itype->second.find(entry);
1693         if (it == itype->second.end() && !fallback.empty())
1694                 it = itype->second.find(fallback);
1695         if (it == itype->second.end())
1696                 return default_format;
1697         return it->second;
1698 }
1699
1700
1701 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
1702         string const & macro) const
1703 {
1704         static string empty;
1705         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
1706         if (itype == cite_macros_.end())
1707                 return empty;
1708         map<string, string>::const_iterator it = itype->second.find(macro);
1709         if (it == itype->second.end())
1710                 return empty;
1711         return it->second;
1712 }
1713
1714
1715 vector<string> const DocumentClass::citeCommands(
1716         CiteEngineType const & type) const
1717 {
1718         vector<CitationStyle> const styles = citeStyles(type);
1719         vector<CitationStyle>::const_iterator it = styles.begin();
1720         vector<CitationStyle>::const_iterator end = styles.end();
1721         vector<string> cmds;
1722         for (; it != end; ++it) {
1723                 CitationStyle const cite = *it;
1724                 cmds.push_back(cite.cmd);
1725         }
1726         return cmds;
1727 }
1728
1729
1730 vector<CitationStyle> const & DocumentClass::citeStyles(
1731         CiteEngineType const & type) const
1732 {
1733         static vector<CitationStyle> empty;
1734         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1735         if (it == cite_styles_.end())
1736                 return empty;
1737         return it->second;
1738 }
1739
1740
1741 /////////////////////////////////////////////////////////////////////////
1742 //
1743 // PageSides
1744 //
1745 /////////////////////////////////////////////////////////////////////////
1746
1747 ostream & operator<<(ostream & os, PageSides p)
1748 {
1749         switch (p) {
1750         case OneSide:
1751                 os << '1';
1752                 break;
1753         case TwoSides:
1754                 os << '2';
1755                 break;
1756         }
1757         return os;
1758 }
1759
1760
1761 } // namespace lyx