]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
Fix problem reported by Vincent, that absence of python can cause
[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/FileName.h"
34 #include "support/filetools.h"
35 #include "support/gettext.h"
36 #include "support/lstrings.h"
37 #include "support/os.h"
38
39 #include <algorithm>
40 #include <fstream>
41 #include <sstream>
42
43 #ifdef ERROR
44 #undef ERROR
45 #endif
46
47 using namespace std;
48 using namespace lyx::support;
49
50 namespace lyx {
51
52 // Keep the changes documented in the Customization manual. 
53 //
54 // If you change this format, then you MUST also make sure that
55 // your changes do not invalidate the hardcoded layout file in 
56 // LayoutFile.cpp. Additions will never do so, but syntax changes
57 // could. See LayoutFileList::addEmptyClass() and, especially, the
58 // definition of the layoutpost string.
59 //
60 int const LAYOUT_FORMAT = 27;
61         
62 namespace {
63
64 class LayoutNamesEqual : public unary_function<Layout, bool> {
65 public:
66         LayoutNamesEqual(docstring const & name)
67                 : name_(name)
68         {}
69         bool operator()(Layout const & c) const
70         {
71                 return c.name() == name_;
72         }
73 private:
74         docstring name_;
75 };
76
77
78 bool layout2layout(FileName const & filename, FileName const & tempfile)
79 {
80         FileName const script = libFileSearch("scripts", "layout2layout.py");
81         if (script.empty()) {
82                 LYXERR0("Could not find layout conversion "
83                           "script layout2layout.py.");
84                 return false;
85         }
86
87         ostringstream command;
88         command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
89                 << ' ' << quoteName(filename.toFilesystemEncoding())
90                 << ' ' << quoteName(tempfile.toFilesystemEncoding());
91         string const command_str = command.str();
92
93         LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
94
95         cmd_ret const ret = runCommand(command_str);
96         if (ret.first != 0) {
97                 LYXERR0("Could not run layout conversion script layout2layout.py.");
98                 return false;
99         }
100         return true;
101 }
102
103
104 string translateRT(TextClass::ReadType rt) 
105 {
106         switch (rt) {
107         case TextClass::BASECLASS:
108                 return "textclass";
109         case TextClass::MERGE:
110                 return "input file";
111         case TextClass::MODULE:
112                 return "module file";
113         case TextClass::VALIDATION:
114                 return "validation";
115         }
116         // shutup warning
117         return string();
118 }
119
120 } // namespace anon
121
122
123 // This string should not be translated here, 
124 // because it is a layout identifier.
125 docstring const TextClass::plain_layout_ = from_ascii("Plain Layout");
126
127
128 InsetLayout DocumentClass::plain_insetlayout_;
129
130
131 /////////////////////////////////////////////////////////////////////////
132 //
133 // TextClass
134 //
135 /////////////////////////////////////////////////////////////////////////
136
137 TextClass::TextClass()
138 {
139         outputType_ = LATEX;
140         outputFormat_ = "latex";
141         columns_ = 1;
142         sides_ = OneSide;
143         secnumdepth_ = 3;
144         tocdepth_ = 3;
145         pagestyle_ = "default";
146         defaultfont_ = sane_font;
147         opt_fontsize_ = "10|11|12";
148         opt_pagestyle_ = "empty|plain|headings|fancy";
149         titletype_ = TITLE_COMMAND_AFTER;
150         titlename_ = "maketitle";
151         loaded_ = false;
152         _("Plain Layout"); // a hack to make this translatable
153 }
154
155
156 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
157 {
158         LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
159         if (!lay.read(lexrc, *this)) {
160                 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
161                 return false;
162         }
163         // Resolve fonts
164         lay.resfont = lay.font;
165         lay.resfont.realize(defaultfont_);
166         lay.reslabelfont = lay.labelfont;
167         lay.reslabelfont.realize(defaultfont_);
168         return true; // no errors
169 }
170
171
172 enum TextClassTags {
173         TC_OUTPUTTYPE = 1,
174         TC_OUTPUTFORMAT,
175         TC_INPUT,
176         TC_STYLE,
177         TC_IFSTYLE,
178         TC_DEFAULTSTYLE,
179         TC_INSETLAYOUT,
180         TC_NOSTYLE,
181         TC_COLUMNS,
182         TC_SIDES,
183         TC_PAGESTYLE,
184         TC_DEFAULTFONT,
185         TC_SECNUMDEPTH,
186         TC_TOCDEPTH,
187         TC_CLASSOPTIONS,
188         TC_PREAMBLE,
189         TC_HTMLPREAMBLE,
190         TC_PROVIDES,
191         TC_REQUIRES,
192         TC_LEFTMARGIN,
193         TC_RIGHTMARGIN,
194         TC_FLOAT,
195         TC_COUNTER,
196         TC_IFCOUNTER,
197         TC_NOFLOAT,
198         TC_TITLELATEXNAME,
199         TC_TITLELATEXTYPE,
200         TC_FORMAT,
201         TC_ADDTOPREAMBLE,
202         TC_ADDTOHTMLPREAMBLE,
203         TC_DEFAULTMODULE,
204         TC_PROVIDESMODULE,
205         TC_EXCLUDESMODULE,
206         TC_HTMLTOCSECTION,
207         TC_CITEFORMAT
208 };
209
210
211 namespace {
212
213         LexerKeyword textClassTags[] = {
214                 { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
215                 { "addtopreamble",     TC_ADDTOPREAMBLE },
216                 { "citeformat",        TC_CITEFORMAT },
217                 { "classoptions",      TC_CLASSOPTIONS },
218                 { "columns",           TC_COLUMNS },
219                 { "counter",           TC_COUNTER },
220                 { "defaultfont",       TC_DEFAULTFONT },
221                 { "defaultmodule",     TC_DEFAULTMODULE },
222                 { "defaultstyle",      TC_DEFAULTSTYLE },
223                 { "excludesmodule",    TC_EXCLUDESMODULE },
224                 { "float",             TC_FLOAT },
225                 { "format",            TC_FORMAT },
226                 { "htmlpreamble",      TC_HTMLPREAMBLE },
227                 { "htmltocsection",    TC_HTMLTOCSECTION },
228                 { "ifcounter",         TC_IFCOUNTER },
229                 { "ifstyle",           TC_IFSTYLE },
230                 { "input",             TC_INPUT },
231                 { "insetlayout",       TC_INSETLAYOUT },
232                 { "leftmargin",        TC_LEFTMARGIN },
233                 { "nofloat",           TC_NOFLOAT },
234                 { "nostyle",           TC_NOSTYLE },
235                 { "outputformat",      TC_OUTPUTFORMAT },
236                 { "outputtype",        TC_OUTPUTTYPE },
237                 { "pagestyle",         TC_PAGESTYLE },
238                 { "preamble",          TC_PREAMBLE },
239                 { "provides",          TC_PROVIDES },
240                 { "providesmodule",    TC_PROVIDESMODULE },
241                 { "requires",          TC_REQUIRES },
242                 { "rightmargin",       TC_RIGHTMARGIN },
243                 { "secnumdepth",       TC_SECNUMDEPTH },
244                 { "sides",             TC_SIDES },
245                 { "style",             TC_STYLE },
246                 { "titlelatexname",    TC_TITLELATEXNAME },
247                 { "titlelatextype",    TC_TITLELATEXTYPE },
248                 { "tocdepth",          TC_TOCDEPTH }
249         };
250         
251 } //namespace anon
252
253
254 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
255 {
256         LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
257         FileName const tempfile = FileName::tempName("convert_layout");
258         bool success = layout2layout(filename, tempfile);
259         if (success)
260                 success = readWithoutConv(tempfile, rt) == OK;
261         tempfile.removeFile();
262         return success;
263 }
264
265
266 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
267 {
268         if (!filename.isReadableFile()) {
269                 lyxerr << "Cannot read layout file `" << filename << "'."
270                        << endl;
271                 return ERROR;
272         }
273
274         LYXERR(Debug::TCLASS, "Reading " + translateRT(rt) + ": " +
275                 to_utf8(makeDisplayPath(filename.absFileName())));
276
277         // Define the plain layout used in table cells, ert, etc. Note that 
278         // we do this before loading any layout file, so that classes can 
279         // override features of this layout if they should choose to do so.
280         if (rt == BASECLASS && !hasLayout(plain_layout_))
281                 layoutlist_.push_back(createBasicLayout(plain_layout_));
282
283         Lexer lexrc(textClassTags);
284         lexrc.setFile(filename);
285         ReturnValues retval = read(lexrc, rt);
286         
287         LYXERR(Debug::TCLASS, "Finished reading " + translateRT(rt) + ": " +
288                         to_utf8(makeDisplayPath(filename.absFileName())));
289
290         return retval;
291 }
292
293
294 bool TextClass::read(FileName const & filename, ReadType rt)
295 {
296         ReturnValues const retval = readWithoutConv(filename, rt);
297         if (retval != FORMAT_MISMATCH)
298                 return retval == OK;
299
300         bool const worx = convertLayoutFormat(filename, rt);
301         if (!worx) {
302                 LYXERR0 ("Unable to convert " << filename << 
303                         " to format " << LAYOUT_FORMAT);
304                 return false;
305         }
306         return true;
307 }
308
309
310 bool TextClass::validate(std::string const & str)
311 {
312         TextClass tc;
313         return tc.read(str, VALIDATION);
314 }
315
316
317 bool TextClass::read(std::string const & str, ReadType rt) 
318 {
319         Lexer lexrc(textClassTags);
320         istringstream is(str);
321         lexrc.setStream(is);
322         ReturnValues retval = read(lexrc, rt);
323
324         if (retval != FORMAT_MISMATCH) 
325                 return retval == OK;
326
327         // write the layout string to a temporary file
328         FileName const tempfile = FileName::tempName("TextClass_read");
329         ofstream os(tempfile.toFilesystemEncoding().c_str());
330         if (!os) {
331                 LYXERR0("Unable to create temporary file");
332                 return false;
333         }
334         os << str;
335         os.close();
336
337         // now try to convert it
338         bool const worx = convertLayoutFormat(tempfile, rt);
339         if (!worx) {
340                 LYXERR0("Unable to convert internal layout information to format " 
341                         << LAYOUT_FORMAT);
342         }
343         tempfile.removeFile();
344         return worx;
345 }
346
347
348 // Reads a textclass structure from file.
349 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt) 
350 {
351         if (!lexrc.isOK())
352                 return ERROR;
353
354         // Format of files before the 'Format' tag was introduced
355         int format = 1;
356         bool error = false;
357
358         // parsing
359         while (lexrc.isOK() && !error) {
360                 int le = lexrc.lex();
361
362                 switch (le) {
363                 case Lexer::LEX_FEOF:
364                         continue;
365
366                 case Lexer::LEX_UNDEF:
367                         lexrc.printError("Unknown TextClass tag `$$Token'");
368                         error = true;
369                         continue;
370
371                 default:
372                         break;
373                 }
374
375                 // used below to track whether we are in an IfStyle or IfCounter tag.
376                 bool ifstyle    = false;
377                 bool ifcounter  = false;
378
379                 switch (static_cast<TextClassTags>(le)) {
380
381                 case TC_FORMAT:
382                         if (lexrc.next())
383                                 format = lexrc.getInteger();
384                         break;
385
386                 case TC_OUTPUTFORMAT:
387                         if (lexrc.next())
388                                 outputFormat_ = lexrc.getString();
389                         break;
390
391                 case TC_OUTPUTTYPE:
392                         readOutputType(lexrc);
393                         switch(outputType_) {
394                         case LATEX:
395                                 outputFormat_ = "latex";
396                                 break;
397                         case DOCBOOK:
398                                 outputFormat_ = "docbook";
399                                 break;
400                         case LITERATE:
401                                 outputFormat_ = "literate";
402                                 break;
403                         }
404                         break;
405
406                 case TC_INPUT: // Include file
407                         if (lexrc.next()) {
408                                 string const inc = lexrc.getString();
409                                 FileName tmp = libFileSearch("layouts", inc,
410                                                             "layout");
411
412                                 if (tmp.empty()) {
413                                         lexrc.printError("Could not find input file: " + inc);
414                                         error = true;
415                                 } else if (!read(tmp, MERGE)) {
416                                         lexrc.printError("Error reading input file: " + tmp.absFileName());
417                                         error = true;
418                                 }
419                         }
420                         break;
421
422                 case TC_DEFAULTSTYLE:
423                         if (lexrc.next()) {
424                                 docstring const name = from_utf8(subst(lexrc.getString(),
425                                                           '_', ' '));
426                                 defaultlayout_ = name;
427                         }
428                         break;
429
430                 case TC_IFSTYLE:
431                         ifstyle = true;
432                         // fall through
433                 case TC_STYLE: {
434                         if (!lexrc.next()) {
435                                 lexrc.printError("No name given for style: `$$Token'.");
436                                 error = true;
437                                 break;
438                         }
439                         docstring const name = from_utf8(subst(lexrc.getString(),
440                                                         '_', ' '));
441                         if (name.empty()) {
442                                 string s = "Could not read name for style: `$$Token' "
443                                         + lexrc.getString() + " is probably not valid UTF-8!";
444                                 lexrc.printError(s);
445                                 Layout lay;
446                                 // Since we couldn't read the name, we just scan the rest
447                                 // of the style and discard it.
448                                 error = !readStyle(lexrc, lay);
449                         } else if (hasLayout(name)) {
450                                 Layout & lay = operator[](name);
451                                 error = !readStyle(lexrc, lay);
452                         } else if (!ifstyle) {
453                                 Layout layout;
454                                 layout.setName(name);
455                                 error = !readStyle(lexrc, layout);
456                                 if (!error)
457                                         layoutlist_.push_back(layout);
458
459                                 if (defaultlayout_.empty()) {
460                                         // We do not have a default layout yet, so we choose
461                                         // the first layout we encounter.
462                                         defaultlayout_ = name;
463                                 }
464                         }
465                         else {
466                                 // this was an ifstyle where we didn't have the style
467                                 // scan the rest and discard it
468                                 Layout lay;
469                                 readStyle(lexrc, lay);
470                         }
471
472                         // reset flag
473                         ifstyle = false;
474                         break;
475                 }
476
477                 case TC_NOSTYLE:
478                         if (lexrc.next()) {
479                                 docstring const style = from_utf8(subst(lexrc.getString(),
480                                                      '_', ' '));
481                                 if (!deleteLayout(style))
482                                         lyxerr << "Cannot delete style `"
483                                                << to_utf8(style) << '\'' << endl;
484                         }
485                         break;
486
487                 case TC_COLUMNS:
488                         if (lexrc.next())
489                                 columns_ = lexrc.getInteger();
490                         break;
491
492                 case TC_SIDES:
493                         if (lexrc.next()) {
494                                 switch (lexrc.getInteger()) {
495                                 case 1: sides_ = OneSide; break;
496                                 case 2: sides_ = TwoSides; break;
497                                 default:
498                                         lyxerr << "Impossible number of page"
499                                                 " sides, setting to one."
500                                                << endl;
501                                         sides_ = OneSide;
502                                         break;
503                                 }
504                         }
505                         break;
506
507                 case TC_PAGESTYLE:
508                         lexrc.next();
509                         pagestyle_ = rtrim(lexrc.getString());
510                         break;
511
512                 case TC_DEFAULTFONT:
513                         defaultfont_ = lyxRead(lexrc);
514                         if (!defaultfont_.resolved()) {
515                                 lexrc.printError("Warning: defaultfont should "
516                                                  "be fully instantiated!");
517                                 defaultfont_.realize(sane_font);
518                         }
519                         break;
520
521                 case TC_SECNUMDEPTH:
522                         lexrc.next();
523                         secnumdepth_ = lexrc.getInteger();
524                         break;
525
526                 case TC_TOCDEPTH:
527                         lexrc.next();
528                         tocdepth_ = lexrc.getInteger();
529                         break;
530
531                 // First step to support options
532                 case TC_CLASSOPTIONS:
533                         readClassOptions(lexrc);
534                         break;
535
536                 case TC_PREAMBLE:
537                         preamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
538                         break;
539
540                 case TC_HTMLPREAMBLE:
541                         htmlpreamble_ = from_utf8(lexrc.getLongString("EndPreamble"));
542                         break;
543                 
544                 case TC_HTMLTOCSECTION:
545                         html_toc_section_ = from_utf8(trim(lexrc.getString()));
546                         break;
547
548                 case TC_ADDTOPREAMBLE:
549                         preamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
550                         break;
551
552                 case TC_ADDTOHTMLPREAMBLE:
553                         htmlpreamble_ += from_utf8(lexrc.getLongString("EndPreamble"));
554                         break;
555
556                 case TC_PROVIDES: {
557                         lexrc.next();
558                         string const feature = lexrc.getString();
559                         lexrc.next();
560                         if (lexrc.getInteger())
561                                 provides_.insert(feature);
562                         else
563                                 provides_.erase(feature);
564                         break;
565                 }
566
567                 case TC_REQUIRES: {
568                         lexrc.eatLine();
569                         vector<string> const req 
570                                 = getVectorFromString(lexrc.getString());
571                         requires_.insert(req.begin(), req.end());
572                         break;
573                 }
574
575                 case TC_DEFAULTMODULE: {
576                         lexrc.next();
577                         string const module = lexrc.getString();
578                         if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
579                                 default_modules_.push_back(module);
580                         break;
581                 }
582
583                 case TC_PROVIDESMODULE: {
584                         lexrc.next();
585                         string const module = lexrc.getString();
586                         if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
587                                 provided_modules_.push_back(module);
588                         break;
589                 }
590
591                 case TC_EXCLUDESMODULE: {
592                         lexrc.next();
593                         string const module = lexrc.getString();
594                         // modules already have their own way to exclude other modules
595                         if (rt == MODULE) {
596                                 LYXERR0("ExcludesModule tag cannot be used in a module!");
597                                 break;
598                         }
599                         if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
600                                 excluded_modules_.push_back(module);
601                         break;
602                 }
603
604                 case TC_LEFTMARGIN:     // left margin type
605                         if (lexrc.next())
606                                 leftmargin_ = lexrc.getDocString();
607                         break;
608
609                 case TC_RIGHTMARGIN:    // right margin type
610                         if (lexrc.next())
611                                 rightmargin_ = lexrc.getDocString();
612                         break;
613
614                 case TC_INSETLAYOUT: {
615                         if (!lexrc.next()) {
616                                 lexrc.printError("No name given for InsetLayout: `$$Token'.");
617                                 error = true;
618                                 break;
619                         }
620                         docstring const name = subst(lexrc.getDocString(), '_', ' ');
621                         if (name.empty()) {
622                                 string s = "Could not read name for InsetLayout: `$$Token' "
623                                         + lexrc.getString() + " is probably not valid UTF-8!";
624                                 lexrc.printError(s);
625                                 InsetLayout il;
626                                 // Since we couldn't read the name, we just scan the rest
627                                 // of the style and discard it.
628                                 il.read(lexrc, *this);
629                                 // Let's try to continue rather than abort.
630                                 // error = true;
631                         } else if (hasInsetLayout(name)) {
632                                 InsetLayout & il = insetlayoutlist_[name];
633                                 error = !il.read(lexrc, *this);
634                         } else {
635                                 InsetLayout il;
636                                 il.setName(name);
637                                 error = !il.read(lexrc, *this);
638                                 if (!error)
639                                         insetlayoutlist_[name] = il;
640                         }
641                         break;
642                 }
643
644                 case TC_FLOAT:
645                         error = !readFloat(lexrc);
646                         break;
647                 
648                 case TC_CITEFORMAT:
649                         readCiteFormat(lexrc);
650                         break;
651
652                 case TC_IFCOUNTER:
653                         ifcounter = true;
654                 case TC_COUNTER:
655                         if (lexrc.next()) {
656                                 docstring const name = lexrc.getDocString();
657                                 if (name.empty()) {
658                                         string s = "Could not read name for counter: `$$Token' "
659                                                         + lexrc.getString() + " is probably not valid UTF-8!";
660                                         lexrc.printError(s.c_str());
661                                         Counter c;
662                                         // Since we couldn't read the name, we just scan the rest
663                                         // and discard it.
664                                         c.read(lexrc);
665                                 } else
666                                         error = !counters_.read(lexrc, name, !ifcounter);
667                         }
668                         else {
669                                 lexrc.printError("No name given for style: `$$Token'.");
670                                 error = true;
671                         }
672                         // reset flag
673                         ifcounter = false;
674                         break;
675
676                 case TC_TITLELATEXTYPE:
677                         readTitleType(lexrc);
678                         break;
679
680                 case TC_TITLELATEXNAME:
681                         if (lexrc.next())
682                                 titlename_ = lexrc.getString();
683                         break;
684
685                 case TC_NOFLOAT:
686                         if (lexrc.next()) {
687                                 string const nofloat = lexrc.getString();
688                                 floatlist_.erase(nofloat);
689                         }
690                         break;
691                 } // end of switch
692
693                 // Note that this is triggered the first time through the loop unless
694                 // we hit a format tag.
695                 if (format != LAYOUT_FORMAT)
696                         return FORMAT_MISMATCH;
697         }
698
699         // at present, we abort if we encounter an error,
700         // so there is no point continuing.
701         if (error)
702                 return ERROR;
703
704         if (rt != BASECLASS)
705                 return (error ? ERROR : OK);
706
707         if (defaultlayout_.empty()) {
708                 LYXERR0("Error: Textclass '" << name_
709                                                 << "' is missing a defaultstyle.");
710                 return ERROR;
711         }
712                 
713         // Try to erase "stdinsets" from the provides_ set. 
714         // The
715         //   Provides stdinsets 1
716         // declaration simply tells us that the standard insets have been
717         // defined. (It's found in stdinsets.inc but could also be used in
718         // user-defined files.) There isn't really any such package. So we
719         // might as well go ahead and erase it.
720         // If we do not succeed, then it was not there, which means that
721         // the textclass did not provide the definitions of the standard
722         // insets. So we need to try to load them.
723         int erased = provides_.erase("stdinsets");
724         if (!erased) {
725                 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
726
727                 if (tmp.empty()) {
728                         frontend::Alert::warning(_("Missing File"),
729                                 _("Could not find stdinsets.inc! This may lead to data loss!"));
730                         error = true;
731                 } else if (!read(tmp, MERGE)) {
732                         frontend::Alert::warning(_("Corrupt File"),
733                                 _("Could not read stdinsets.inc! This may lead to data loss!"));
734                         error = true;
735                 }
736         }
737
738         min_toclevel_ = Layout::NOT_IN_TOC;
739         max_toclevel_ = Layout::NOT_IN_TOC;
740         const_iterator lit = begin();
741         const_iterator len = end();
742         for (; lit != len; ++lit) {
743                 int const toclevel = lit->toclevel;
744                 if (toclevel != Layout::NOT_IN_TOC) {
745                         if (min_toclevel_ == Layout::NOT_IN_TOC)
746                                 min_toclevel_ = toclevel;
747                         else
748                                 min_toclevel_ = min(min_toclevel_, toclevel);
749                         max_toclevel_ = max(max_toclevel_, toclevel);
750                 }
751         }
752         LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
753                 << ", maximum is " << max_toclevel_);
754
755         return (error ? ERROR : OK);
756 }
757
758
759 void TextClass::readTitleType(Lexer & lexrc)
760 {
761         LexerKeyword titleTypeTags[] = {
762                 { "commandafter", TITLE_COMMAND_AFTER },
763                 { "environment",  TITLE_ENVIRONMENT }
764         };
765
766         PushPopHelper pph(lexrc, titleTypeTags);
767
768         int le = lexrc.lex();
769         switch (le) {
770         case Lexer::LEX_UNDEF:
771                 lexrc.printError("Unknown output type `$$Token'");
772                 break;
773         case TITLE_COMMAND_AFTER:
774         case TITLE_ENVIRONMENT:
775                 titletype_ = static_cast<TitleLatexType>(le);
776                 break;
777         default:
778                 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
779                 break;
780         }
781 }
782
783
784 void TextClass::readOutputType(Lexer & lexrc)
785 {
786         LexerKeyword outputTypeTags[] = {
787                 { "docbook",  DOCBOOK },
788                 { "latex",    LATEX },
789                 { "literate", LITERATE }
790         };
791
792         PushPopHelper pph(lexrc, outputTypeTags);
793
794         int le = lexrc.lex();
795         switch (le) {
796         case Lexer::LEX_UNDEF:
797                 lexrc.printError("Unknown output type `$$Token'");
798                 return;
799         case LATEX:
800         case DOCBOOK:
801         case LITERATE:
802                 outputType_ = static_cast<OutputType>(le);
803                 break;
804         default:
805                 LYXERR0("Unhandled value " << le);
806                 break;
807         }
808 }
809
810
811 void TextClass::readClassOptions(Lexer & lexrc)
812 {
813         enum {
814                 CO_FONTSIZE = 1,
815                 CO_PAGESTYLE,
816                 CO_OTHER,
817                 CO_HEADER,
818                 CO_END
819         };
820
821         LexerKeyword classOptionsTags[] = {
822                 {"end",       CO_END },
823                 {"fontsize",  CO_FONTSIZE },
824                 {"header",    CO_HEADER },
825                 {"other",     CO_OTHER },
826                 {"pagestyle", CO_PAGESTYLE }
827         };
828
829         lexrc.pushTable(classOptionsTags);
830         bool getout = false;
831         while (!getout && lexrc.isOK()) {
832                 int le = lexrc.lex();
833                 switch (le) {
834                 case Lexer::LEX_UNDEF:
835                         lexrc.printError("Unknown ClassOption tag `$$Token'");
836                         continue;
837                 default:
838                         break;
839                 }
840                 switch (le) {
841                 case CO_FONTSIZE:
842                         lexrc.next();
843                         opt_fontsize_ = rtrim(lexrc.getString());
844                         break;
845                 case CO_PAGESTYLE:
846                         lexrc.next();
847                         opt_pagestyle_ = rtrim(lexrc.getString());
848                         break;
849                 case CO_OTHER:
850                         lexrc.next();
851                         if (options_.empty())
852                                 options_ = lexrc.getString();
853                         else
854                                 options_ += ',' + lexrc.getString();
855                         break;
856                 case CO_HEADER:
857                         lexrc.next();
858                         class_header_ = subst(lexrc.getString(), "&quot;", "\"");
859                         break;
860                 case CO_END:
861                         getout = true;
862                         break;
863                 }
864         }
865         lexrc.popTable();
866 }
867
868
869 void TextClass::readCiteFormat(Lexer & lexrc)
870 {
871         string etype;
872         string definition;
873         while (lexrc.isOK()) {
874                 lexrc.next();
875                 etype = lexrc.getString();
876                 if (!lexrc.isOK() || compare_ascii_no_case(etype, "end") == 0)
877                         break;
878                 lexrc.eatLine();
879                 definition = lexrc.getString();
880                 char initchar = etype[0];
881                 if (initchar == '#')
882                         continue;
883                 if (initchar == '!' || initchar == '_')
884                         cite_macros_[etype] = definition;
885                 else
886                         cite_formats_[etype] = definition;
887         }
888 }
889
890
891 bool TextClass::readFloat(Lexer & lexrc)
892 {
893         enum {
894                 FT_TYPE = 1,
895                 FT_NAME,
896                 FT_PLACEMENT,
897                 FT_EXT,
898                 FT_WITHIN,
899                 FT_STYLE,
900                 FT_LISTNAME,
901                 FT_NEEDSFLOAT,
902                 FT_HTMLSTYLE,
903                 FT_HTMLATTR,
904                 FT_HTMLTAG,
905                 FT_LISTCOMMAND,
906                 FT_REFPREFIX,
907                 FT_END
908         };
909
910         LexerKeyword floatTags[] = {
911                 { "end", FT_END },
912                 { "extension", FT_EXT },
913                 { "guiname", FT_NAME },
914                 { "htmlattr", FT_HTMLATTR },
915                 { "htmlstyle", FT_HTMLSTYLE },
916                 { "htmltag", FT_HTMLTAG },
917                 { "listcommand", FT_LISTCOMMAND },
918                 { "listname", FT_LISTNAME },
919                 { "needsfloatpkg", FT_NEEDSFLOAT },
920                 { "numberwithin", FT_WITHIN },
921                 { "placement", FT_PLACEMENT },
922                 { "refprefix", FT_REFPREFIX },
923                 { "style", FT_STYLE },
924                 { "type", FT_TYPE }
925         };
926
927         lexrc.pushTable(floatTags);
928
929         string ext;
930         string htmlattr;
931         string htmlstyle;
932         string htmltag;
933         string listname;
934         string listcommand;
935         string name;
936         string placement;
937         string refprefix;
938         string style;
939         string type;
940         string within;
941         bool needsfloat = true;
942
943         bool getout = false;
944         while (!getout && lexrc.isOK()) {
945                 int le = lexrc.lex();
946                 switch (le) {
947                 case Lexer::LEX_UNDEF:
948                         lexrc.printError("Unknown float tag `$$Token'");
949                         continue;
950                 default:
951                         break;
952                 }
953                 switch (le) {
954                 case FT_TYPE:
955                         lexrc.next();
956                         type = lexrc.getString();
957                         if (floatlist_.typeExist(type)) {
958                                 Floating const & fl = floatlist_.getType(type);
959                                 placement = fl.placement();
960                                 ext = fl.ext();
961                                 within = fl.within();
962                                 style = fl.style();
963                                 name = fl.name();
964                                 listname = fl.listName();
965                                 needsfloat = fl.needsFloatPkg();
966                                 listcommand = fl.listCommand();
967                                 refprefix = fl.refPrefix();
968                         } 
969                         break;
970                 case FT_NAME:
971                         lexrc.next();
972                         name = lexrc.getString();
973                         break;
974                 case FT_PLACEMENT:
975                         lexrc.next();
976                         placement = lexrc.getString();
977                         break;
978                 case FT_EXT:
979                         lexrc.next();
980                         ext = lexrc.getString();
981                         break;
982                 case FT_WITHIN:
983                         lexrc.next();
984                         within = lexrc.getString();
985                         if (within == "none")
986                                 within.erase();
987                         break;
988                 case FT_STYLE:
989                         lexrc.next();
990                         style = lexrc.getString();
991                         break;
992                 case FT_LISTCOMMAND:
993                         lexrc.next();
994                         listcommand = lexrc.getString();
995                         break;
996                 case FT_REFPREFIX:
997                         lexrc.next();
998                         refprefix = lexrc.getString();
999                         break;
1000                 case FT_LISTNAME:
1001                         lexrc.next();
1002                         listname = lexrc.getString();
1003                         break;
1004                 case FT_NEEDSFLOAT:
1005                         lexrc.next();
1006                         needsfloat = lexrc.getBool();
1007                         break;
1008                 case FT_HTMLATTR:
1009                         lexrc.next();
1010                         htmlattr = lexrc.getString();
1011                         break;
1012                 case FT_HTMLSTYLE:
1013                         lexrc.next();
1014                         htmlstyle = lexrc.getLongString("EndHTMLStyle");
1015                         break;
1016                 case FT_HTMLTAG:
1017                         lexrc.next();
1018                         htmltag = lexrc.getString();
1019                         break;
1020                 case FT_END:
1021                         getout = true;
1022                         break;
1023                 }
1024         }
1025
1026         lexrc.popTable();
1027
1028         // Here we have a full float if getout == true
1029         if (getout) {
1030                 if (!needsfloat && listcommand.empty())
1031                         LYXERR0("The layout does not provide a list command " <<
1032                                 "for the builtin float `" << type << "'. LyX will " <<
1033                                 "not be able to produce a float list.");
1034                 Floating fl(type, placement, ext, within, style, name, 
1035                                 listname, listcommand, refprefix, 
1036                                 htmltag, htmlattr, htmlstyle, needsfloat);
1037                 floatlist_.newFloat(fl);
1038                 // each float has its own counter
1039                 counters_.newCounter(from_ascii(type), from_ascii(within),
1040                                       docstring(), docstring());
1041                 // also define sub-float counters
1042                 docstring const subtype = "sub-" + from_ascii(type);
1043                 counters_.newCounter(subtype, from_ascii(type),
1044                                       "\\alph{" + subtype + "}", docstring());
1045         }
1046         return getout;
1047 }
1048
1049
1050 string const & TextClass::prerequisites() const
1051
1052         if (contains(prerequisites_, ',')) {
1053                 vector<string> const pres = getVectorFromString(prerequisites_);
1054                 prerequisites_ = getStringFromVector(pres, "\n\t");
1055         }
1056         return prerequisites_; 
1057 }
1058
1059 bool TextClass::hasLayout(docstring const & n) const
1060 {
1061         docstring const name = n.empty() ? defaultLayoutName() : n;
1062
1063         return find_if(layoutlist_.begin(), layoutlist_.end(),
1064                        LayoutNamesEqual(name))
1065                 != layoutlist_.end();
1066 }
1067
1068
1069 bool TextClass::hasInsetLayout(docstring const & n) const
1070 {
1071         if (n.empty()) 
1072                 return false;
1073         InsetLayouts::const_iterator it = insetlayoutlist_.begin();
1074         InsetLayouts::const_iterator en = insetlayoutlist_.end();
1075         for (; it != en; ++it)
1076                 if (n == it->first)
1077                         return true;
1078         return false;
1079 }
1080
1081
1082 Layout const & TextClass::operator[](docstring const & name) const
1083 {
1084         LASSERT(!name.empty(), /**/);
1085
1086         const_iterator it = 
1087                 find_if(begin(), end(), LayoutNamesEqual(name));
1088
1089         if (it == end()) {
1090                 lyxerr << "We failed to find the layout '" << to_utf8(name)
1091                        << "' in the layout list. You MUST investigate!"
1092                        << endl;
1093                 for (const_iterator cit = begin(); cit != end(); ++cit)
1094                         lyxerr  << " " << to_utf8(cit->name()) << endl;
1095
1096                 // we require the name to exist
1097                 LASSERT(false, /**/);
1098         }
1099
1100         return *it;
1101 }
1102
1103
1104 Layout & TextClass::operator[](docstring const & name)
1105 {
1106         LASSERT(!name.empty(), /**/);
1107
1108         iterator it = find_if(begin(), end(), LayoutNamesEqual(name));
1109
1110         if (it == end()) {
1111                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1112                        << "' in the layout list. You MUST investigate!");
1113                 for (const_iterator cit = begin(); cit != end(); ++cit)
1114                         LYXERR0(" " << to_utf8(cit->name()));
1115
1116                 // we require the name to exist
1117                 LASSERT(false, /**/);
1118         }
1119
1120         return *it;
1121 }
1122
1123
1124 bool TextClass::deleteLayout(docstring const & name)
1125 {
1126         if (name == defaultLayoutName() || name == plainLayoutName())
1127                 return false;
1128
1129         LayoutList::iterator it =
1130                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1131                           LayoutNamesEqual(name));
1132
1133         LayoutList::iterator end = layoutlist_.end();
1134         bool const ret = (it != end);
1135         layoutlist_.erase(it, end);
1136         return ret;
1137 }
1138
1139
1140 // Load textclass info if not loaded yet
1141 bool TextClass::load(string const & path) const
1142 {
1143         if (loaded_)
1144                 return true;
1145
1146         // Read style-file, provided path is searched before the system ones
1147         // If path is a file, it is loaded directly.
1148         FileName layout_file(path);
1149         if (!path.empty() && !layout_file.isReadableFile())
1150                 layout_file = FileName(addName(path, name_ + ".layout"));
1151         if (layout_file.empty() || !layout_file.exists())
1152                 layout_file = libFileSearch("layouts", name_, "layout");
1153         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1154
1155         if (!loaded_) {
1156                 lyxerr << "Error reading `"
1157                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1158                        << "'\n(Check `" << name_
1159                        << "')\nCheck your installation and "
1160                           "try Options/Reconfigure..." 
1161                        << endl;
1162         }
1163
1164         return loaded_;
1165 }
1166
1167
1168 void DocumentClass::addLayoutIfNeeded(docstring const & n) const
1169 {
1170         if (!hasLayout(n))
1171                 layoutlist_.push_back(createBasicLayout(n, true));
1172 }
1173
1174
1175 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const 
1176 {
1177         // FIXME The fix for the InsetLayout part of 4812 would be here:
1178         // Add the InsetLayout to the document class if it is not found.
1179         docstring n = name;
1180         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1181         while (!n.empty()) {
1182                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1183                 if (cit != cen && cit->first == n)
1184                         return cit->second;
1185                 size_t i = n.find(':');
1186                 if (i == string::npos)
1187                         break;
1188                 n = n.substr(0, i);
1189         }
1190         return plain_insetlayout_;
1191 }
1192
1193
1194 docstring const & TextClass::defaultLayoutName() const
1195 {
1196         // This really should come from the actual layout... (Lgb)
1197         return defaultlayout_;
1198 }
1199
1200
1201 Layout const & TextClass::defaultLayout() const
1202 {
1203         return operator[](defaultLayoutName());
1204 }
1205
1206
1207 bool TextClass::isDefaultLayout(Layout const & layout) const 
1208 {
1209         return layout.name() == defaultLayoutName();
1210 }
1211
1212
1213 bool TextClass::isPlainLayout(Layout const & layout) const 
1214 {
1215         return layout.name() == plainLayoutName();
1216 }
1217
1218
1219 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1220 {
1221         static Layout * defaultLayout = NULL;
1222
1223         if (defaultLayout) {
1224                 defaultLayout->setUnknown(unknown);
1225                 defaultLayout->setName(name);
1226                 return *defaultLayout;
1227         }
1228
1229         static char const * s = "Margin Static\n"
1230                         "LatexType Paragraph\n"
1231                         "LatexName dummy\n"
1232                         "Align Block\n"
1233                         "AlignPossible Left, Right, Center\n"
1234                         "LabelType No_Label\n"
1235                         "End";
1236         istringstream ss(s);
1237         Lexer lex(textClassTags);
1238         lex.setStream(ss);
1239         defaultLayout = new Layout;
1240         defaultLayout->setUnknown(unknown);
1241         defaultLayout->setName(name);
1242         if (!readStyle(lex, *defaultLayout)) {
1243                 // The only way this happens is because the hardcoded layout above
1244                 // is wrong.
1245                 LASSERT(false, /**/);
1246         };
1247         return *defaultLayout;
1248 }
1249
1250
1251 /////////////////////////////////////////////////////////////////////////
1252 //
1253 // DocumentClassBundle
1254 //
1255 /////////////////////////////////////////////////////////////////////////
1256
1257 DocumentClassBundle::~DocumentClassBundle()
1258 {
1259         for (size_t i = 0; i != documentClasses_.size(); ++i)
1260                 delete documentClasses_[i];
1261         documentClasses_.clear();
1262 }
1263
1264 DocumentClass & DocumentClassBundle::newClass(LayoutFile const & baseClass)
1265 {
1266         DocumentClass * dc = new DocumentClass(baseClass);
1267         documentClasses_.push_back(dc);
1268         return *documentClasses_.back();
1269 }
1270
1271
1272 DocumentClassBundle & DocumentClassBundle::get()
1273 {
1274         static DocumentClassBundle singleton; 
1275         return singleton; 
1276 }
1277
1278
1279 DocumentClass & DocumentClassBundle::makeDocumentClass(
1280                 LayoutFile const & baseClass, LayoutModuleList const & modlist)
1281 {
1282         DocumentClass & doc_class = newClass(baseClass);
1283         LayoutModuleList::const_iterator it = modlist.begin();
1284         LayoutModuleList::const_iterator en = modlist.end();
1285         for (; it != en; it++) {
1286                 string const modName = *it;
1287                 LyXModule * lm = theModuleList[modName];
1288                 if (!lm) {
1289                         docstring const msg =
1290                                                 bformat(_("The module %1$s has been requested by\n"
1291                                                 "this document but has not been found in the list of\n"
1292                                                 "available modules. If you recently installed it, you\n"
1293                                                 "probably need to reconfigure LyX.\n"), from_utf8(modName));
1294                         frontend::Alert::warning(_("Module not available"), msg);
1295                         continue;
1296                 }
1297                 if (!lm->isAvailable()) {
1298                         docstring const msg =
1299                                                 bformat(_("The module %1$s requires a package that is\n"
1300                                                 "not available in your LaTeX installation. LaTeX output\n"
1301                                                 "may not be possible.\n"), from_utf8(modName));
1302                         frontend::Alert::warning(_("Package not available"), msg);
1303                 }
1304                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1305                 if (!doc_class.read(layout_file, TextClass::MODULE)) {
1306                         docstring const msg =
1307                                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1308                         frontend::Alert::warning(_("Read Error"), msg);
1309                 }
1310         }
1311         return doc_class;
1312 }
1313
1314
1315 /////////////////////////////////////////////////////////////////////////
1316 //
1317 // DocumentClass
1318 //
1319 /////////////////////////////////////////////////////////////////////////
1320
1321 DocumentClass::DocumentClass(LayoutFile const & tc)
1322         : TextClass(tc)
1323 {}
1324
1325
1326 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1327 {
1328         LayoutList::const_iterator it  = layoutlist_.begin();
1329         LayoutList::const_iterator end = layoutlist_.end();
1330         for (; it != end; ++it)
1331                 if (it->latexname() == lay)
1332                         return true;
1333         return false;
1334 }
1335
1336
1337 bool DocumentClass::provides(string const & p) const
1338 {
1339         return provides_.find(p) != provides_.end();
1340 }
1341
1342
1343 bool DocumentClass::hasTocLevels() const
1344 {
1345         return min_toclevel_ != Layout::NOT_IN_TOC;
1346 }
1347
1348
1349 Layout const & DocumentClass::htmlTOCLayout() const
1350 {
1351         if (html_toc_section_.empty()) {
1352                 // we're going to look for the layout with the minimum toclevel
1353                 TextClass::LayoutList::const_iterator lit = begin();
1354                 TextClass::LayoutList::const_iterator const len = end();
1355                 int minlevel = 1000;
1356                 Layout const * lay = NULL;
1357                 for (; lit != len; ++lit) {
1358                         int const level = lit->toclevel;
1359                         // we don't want Part
1360                         if (level == Layout::NOT_IN_TOC || level < 0 || level >= minlevel)
1361                                 continue;
1362                         lay = &*lit;
1363                         minlevel = level;
1364                 }
1365                 if (lay)
1366                         html_toc_section_ = lay->name();
1367                 else
1368                         // hmm. that is very odd, so we'll do our best
1369                         html_toc_section_ = defaultLayoutName();
1370         }
1371         return operator[](html_toc_section_);
1372 }
1373
1374
1375 string const & DocumentClass::getCiteFormat(string const & entry_type) const
1376 {
1377         static string default_format = N_("{%author%[[%author%, ]][[{%editor%[[%editor%, ed., ]]}]]}\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]][[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}.");
1378         
1379         map<string, string>::const_iterator it = cite_formats_.find(entry_type);
1380         if (it != cite_formats_.end())
1381                 return it->second;
1382         return default_format;
1383 }
1384
1385
1386 string const & DocumentClass::getCiteMacro(string const & macro) const
1387 {
1388         static string empty;
1389         map<string, string>::const_iterator it = cite_macros_.find(macro);
1390         if (it != cite_macros_.end())
1391                 return it->second;
1392         return empty;
1393 }
1394
1395
1396 /////////////////////////////////////////////////////////////////////////
1397 //
1398 // PageSides
1399 //
1400 /////////////////////////////////////////////////////////////////////////
1401
1402 ostream & operator<<(ostream & os, PageSides p)
1403 {
1404         switch (p) {
1405         case OneSide:
1406                 os << '1';
1407                 break;
1408         case TwoSides:
1409                 os << '2';
1410                 break;
1411         }
1412         return os;
1413 }
1414
1415
1416 } // namespace lyx