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