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